Type error when processing graphql result - ocaml

I just started playing with reasonML and graphql and have built a simple react component that retrieves data from a world cup API. My code is below:
[#bs.module] external gql: ReasonApolloTypes.gql = "graphql-tag";
module GetMatches = [%graphql
{|
query getMatches($id: ID!){
matches(id: $id) {
date
homeTeam {name}
awayTeam {name}
homeScore
awayScore
goals {
scorer {
name
}
time
}
}
}
|}
];
module GetMatchesQuery = ReasonApollo.CreateQuery(GetMatches);
let component = ReasonReact.statelessComponent("Matches");
let make = _children => {
...component,
render: _self => {
let matchParam = GetMatches.make(~id="300331511", ());
<GetMatchesQuery variables=matchParam##variables>
...{
({result}) =>
switch (result) {
| Loading => <div> {ReasonReact.string("Loading")} </div>
| Error(error) =>
<div> {ReasonReact.string(error##message)} </div>
| Data(response) => <Matches matches=response##matches />
}
}
</GetMatchesQuery>;
},
};
Matches Component
let component = ReasonReact.statelessComponent("Matches");
let make = (~matches, _children) => {
...component,
render: _self =>
<div>
{
matches
|> Js.Array.map(match => <Match match key=match##id />)
|> ReasonReact.array
}
</div>,
};
But I'm getting this error:
This has type:
option(Js.Array.t(option({. "awayScore": option(int),
"awayTeam": option({. "name": option(string)}),
"date": option(string),
"goals": option(Js.Array.t(option({. "scorer":
option(
{. "name":
option(
string)}),
"time":
option(
string)}))),
"homeScore": option(int),
"homeTeam": option({. "name": option(string)})})))
But somewhere wanted:
Js.Array.t(Js.t(({.. id: string} as 'a))) (defined as array(Js.t('a)))
I added Js.log(response##matches); and got this in the console

I'm pretty sure there's some context missing here, and that you have something like open Belt at the top of your file.
The type error says response##matches is an array in an option, but also that the array elements themselves are in options, which is super weird. So my theory is that the code generated by graphql-ppx uses something like array indexing, which translates to a call to Array.get that in the standard library throws an exception if out of bounds, but in Belt, and many other standard libraries replacements, returns an option.
This is one of many problems with using ppxs. It generates code not in isolation and can interact weirdly with the rest of your code, and it's not easy to debug.

Related

How do I avoid calling ".clone()" on same String in match multiple times?

Background:
I have some code (Rust) that finds (Regex) matches and assigns the found values to fields in a struct named Article (where all fields are of type String):
pub struct Article {
// user facing data
title: String,
category: String,
subcategory: String,
genre: String,
published: String,
estimated_read_time: String,
description: String,
tags: String,
keywords: String,
image: String,
artwork_credit: String,
// meta data
metas: String,
// location
path: String,
slug: String,
// file data
content: String
}
A regular expression ("//\- define (.*?): (.*?)\n") is used to extract comments from the article's template that define data for that article:
// iterate through HTML property pattern matches
for capture in re_define.captures_iter(&file_content as &str) {
// remove the declaration from the the HTML output
article_content = article_content.replace(&capture[0].to_string(), "");
// get the property value
let property_value: &String = &capture[2].to_string();
// determine what field to assign the property to and assign it
match capture[1].to_lowercase().as_str() {
"title" => article.title = property_value.clone(),
"category" => article.category = property_value.clone(),
"subcategory" => article.subcategory = property_value.clone(),
"genre" => article.genre = property_value.clone(),
"published" => article.published = property_value.clone(),
"estimated_read_time" => article.estimated_read_time = property_value.clone(),
"description" => article.description = property_value.clone(),
"tags" => article.tags = property_value.clone(),
"keywords" => article.keywords = property_value.clone(),
"image" => article.image = property_value.clone(),
unknown_property # _ => {
println!("Ignoring unknown property: {}", &unknown_property);
}
}
}
Note: article is an instance of Article.
Issue:
The code works but what I'm concerned about the following part:
"title" => article.title = property_value.clone(),
"category" => article.category = property_value.clone(),
"subcategory" => article.subcategory = property_value.clone(),
"genre" => article.genre = property_value.clone(),
"published" => article.published = property_value.clone(),
"estimated_read_time" => article.estimated_read_time = property_value.clone(),
"description" => article.description = property_value.clone(),
"tags" => article.tags = property_value.clone(),
"keywords" => article.keywords = property_value.clone(),
"image" => article.image = property_value.clone(),
It calls .clone() on the same String (property_value) for every match (10 matches per article template), for every article template (a couple dozen templates in total), and I don't think it's the most efficient way to do it.
Note: I'm not sure if match is cloning for non-matches.
What I tried:
I tried referencing the property_value String, but I got an error for each reference.
Error from IDE (VS Code):
mismatched types
expected struct `std::string::String`, found `&&std::string::String`
expected due to the type of this binding
try using a conversion method: `(`, `).to_string()`
Error from cargo check:
error[E0308]: mismatched types
--> src/article.rs:84:38
|
84 | "image" => article.image = &property_value,
| ------------- ^^^^^^^^^^^^^^^ expected struct `std::string::String`, found `&&std::string::String`
| |
| expected due to the type of this binding
|
help: try using a conversion method
|
84 | "image" => article.image = (&property_value).to_string(),
| + +++++++++++++
I did try using .to_string(), but I'm not sure if converting a String to the same type is the most efficient to do it either.
Question:
How do I avoid calling .clone() on property_value so many times?
Going by the types, you should just be able to drop the borrow in property_value and then you don't need the .clone()s.
let property_value: &String = &capture[2].to_string();
// change to
let property_value: String = capture[2].to_string();
// or just simply
let property_value = capture[2].to_string();
I'm assuming this was added as capture[2] returns a str (non-sized type) which would require the & but with to_string() it converts to the owned type String which is fine on it's own. This wont have any performance effect as to_string() copies anyway.

Regex in logstash mutate gsub to replace a character in a string

I am trying to get double quotes (placed at random places) from a string replaced with something else.
This is the logline-
msg="AUT30544: User chose to proceed on the sign-in notification page "Sign-In Notification Message""
Actually this was part of KV parsing in logstash's filter section. If you notice there is a quoted string inside of a string that itself is in double-quotes.
However, Below string gets correctly parsed in KV-
msg="AUT23278: User Limit realm restrictions successfully passed for /google_auth "
Now I created a regex to remove the double-quotes in problematic string-
https://regex101.com/r/o00oot/1/
Applied it in logstash but nothing changed.
Below is my config file-
input {
tcp {
port => 1301
}
}
filter {
if "type=vpn" in [message] {
dissect {
mapping => { "message" => "%{reserved} id=firewall %{message1}" }
}
#mutate { gsub => ["message1",':'," "] }
#mutate { gsub => ["message1",'"',''] }
mutate {gsub => ["msg","(.*)\"(.*)\"(\")", "\1 '\2 '\3"] }
kv { source => "message1" value_split => "=" whitespace => "strict" } #field_split => " " remove_char_value => '"' }
geoip { source => "src" }
# \/ end of if vpn type log
}
else { drop {} }
}
A similar logline that I could capture using tcpdump is-
<134>Oct 2 11:24:45 1xx.xx.43.101 1 2021-10-02T11:24:45+05:30 canopus.domain1.com2 PulseSecure: - - - id=firewall time="2021-10-02 11:24:45" pri=6 fw=172.20.43.101 vpn=ive user=user1 realm="google_auth" roles="" proto=auth src=2xx.176.114.94 dst= dstname= type=vpn op= arg="" result= sent= rcvd= agent="" duration= msg="AUT30544: User chose to proceed on the sign-in notification page "Sign-In Notification Message""
The stdout of same kind of message on stdout. I can see the double-quotes being escaped but still they create problem in parsing.
{
"type" => "vpn",
"user" => "user1",
"fw" => "1xx.xx.43.101",
"host" => "1xx.xx.4.63",
"realm" => "google_auth",
"src" => "1xx.66.50.112",
"port" => 33003,
"#version" => "1",
"message" => "<13>Oct 2 11:54:39 1xx.xx.43.101 396 <134>1 2021-10-02T11:54:39+05:30 canopus.domain1.com2 PulseSecure: - - - id=firewall time=\"2021-10-02 11:54:39\" pri=6 fw=1xx.xx.43.101 vpn=ive user=user1 realm=\"google_auth\" roles=\"\" proto=auth src=1xx.66.50.112 dst= dstname= type=vpn op= arg=\"\" result= sent= rcvd= agent=\"\" duration= msg=\"AUT30544: User chose to proceed on the sign-in notification page \"Sign-In Notification Message\"\"",
"geoip" => {
"location" => {
"lon" => 77.5937,
"lat" => 12.9719
},
If someone knows a KV plugin's native solution to this problem, I dont need to go through hassles of regex in gsub.
I'm not sure if you can use kv on whole message as you have, try to split it up so you get key/value part of the message in separate field and then use kv on it. That being said, I would suggest you to skip using gsub here completely because there is option called trim_value for kv filter.
With that option your configuration would look something like this. Disclaimer, this is not tested, maybe you'll have to play with regex inside of trim_value, but this is easier way to handle it.
input {
tcp {
port => 1301
}
}
filter {
if "type=vpn" in [message] {
dissect {
mapping => { "message" => "%{reserved} id=firewall %{message1}" }
}
kv {
source => "message1"
value_split => "="
whitespace => "strict"
trim_value => "\\\""
}
geoip {
source => "src"
}
}
else {
drop { }
}
}

Mongodb returns all fields even with projection specified

Update:
I want to only return all documents that fit four characters of a given username that is entered. So if I have a list of usernames and I type in mango3333, all usernames that are starting with "mang" should be returned. I used a regexp for that, and now I want to only return for example the username and the id of that document and not all fields, but it returns all fields.
An example document looks like this:
{"_id":{"$oid":"5d75299b0d01830"},
"User":
{ "username":"mango",
"userid":"8b8d25d3-3fe6-4d1c",
"token":"token",
"pw":"password",
"statusmessage":"",
"lastlogin":{"$date":{"$numberLong":"1567959451354"}},
"registered":{"$date":{"$numberLong":"1567959451354"}
}
This is my query:
const db = dbClient.db(dbName);
const regexp = new RegExp(username, "gi");
db.collection(collectionName).find({ "User.Username": regexp }, { "User.Username": 1, "User.UserID": 1, "User.Statusmessage": 1 })
.toArray()
.then(result => {
console.log(result);
})
.catch(err => console.error(`Failed to get results: ${err}`));
What am I doing wrong?
The 2nd portion of the find method is an options object, not just projection. The projection portion of the query will need to be specified in this options object. Try the following:
db.collection(collectionName).find(
{ "User.Username": regexp },
{
projection: {
"User.Username": 1,
"User.UserID": 1,
"User.Statusmessage": 1
}
}
)
.toArray()
.then(result => {
console.log(result);
})
See https://mongodb.github.io/node-mongodb-native/3.3/api/Collection.html#find

babel-plugin-react-intl: Extract strings to a single file

Currently while using babel-plugin-react-intl, separate json for every component is created with 'id', 'description' and 'defaultMessage'. What I need is that only a single json to be created which contains a single object with all the 'id' as the 'key' and 'defaultMessage' as the 'value'
Present situation:
ComponentA.json
[
{
"id": "addEmoticonA",
"description": "Add emoticon",
"defaultMessage": "Insert Emoticon"
},
{
"id": "addPhotoA",
"description": "Add photo",
"defaultMessage": "Insert photo"
}
]
ComponentB.json
[
{
"id": "addEmoticonB",
"description": "Add emoji",
"defaultMessage": "Insert Emoji"
},
{
"id": "addPhotoB",
"description": "Add picture",
"defaultMessage": "Insert picture"
}
]
What I need for translation.
final.json
{
"addEmoticonA": "Insert Emoticon",
"addPhotoA": "Insert photo",
"addEmoticonB": "Insert Emoji",
"addPhotoB": "Insert picture"
}
Is there any way to accomplish this task? May it be by using python script or anything. i.e to make a single json file from different json files. Or to directly make a single json file using babel-plugin-react-intl
There is a translations manager that will do this.
Or for a custom option see below
The script below which is based on this script goes through the translation messages created by
babel-plugin-react-intl and creates js files that contain all messages from all components in the json format.
import fs from 'fs'
import {
sync as globSync
}
from 'glob'
import {
sync as mkdirpSync
}
from 'mkdirp'
import * as i18n from '../lib/i18n'
const MESSAGES_PATTERN = './_translations/**/*.json'
const LANG_DIR = './_translations/lang/'
// Ensure output folder exists
mkdirpSync(LANG_DIR)
// Aggregates the default messages that were extracted from the example app's
// React components via the React Intl Babel plugin. An error will be thrown if
// there are messages in different components that use the same `id`. The result
// is a flat collection of `id: message` pairs for the app's default locale.
let defaultMessages = globSync(MESSAGES_PATTERN)
.map(filename => fs.readFileSync(filename, 'utf8'))
.map(file => JSON.parse(file))
.reduce((collection, descriptors) => {
descriptors.forEach(({
id, defaultMessage, description
}) => {
if (collection.hasOwnProperty(id))
throw new Error(`Duplicate message id: ${id}`)
collection[id] = {
defaultMessage, description
}
})
return collection
}, {})
// Sort keys by name
const messageKeys = Object.keys(defaultMessages)
messageKeys.sort()
defaultMessages = messageKeys.reduce((acc, key) => {
acc[key] = defaultMessages[key]
return acc
}, {})
// Build the JSON document for the available languages
i18n.en = messageKeys.reduce((acc, key) => {
acc[key] = defaultMessages[key].defaultMessage
return acc
}, {})
Object.keys(i18n).forEach(lang => {
const langDoc = i18n[lang]
const units = Object.keys(defaultMessages).map((id) => [id, defaultMessages[id]]).reduce((collection, [id]) => {
collection[id] = langDoc[id] || '';
return collection;
}, {});
fs.writeFileSync(`${LANG_DIR}${lang}.json`, JSON.stringify(units, null, 2))
})
You can use babel-plugin-react-intl-extractor for aggregate your translations in single file. Also it provides autorecompile translation files on each change of your messages.

Coupon Magento API Soap

I’ve a problem with the Coupon API when i make :
$couponCode = "test";
$resultCartCoupon = $proxy->call($sessionId, "cart_coupon.add", array($shoppingCartId, $couponCode));
I always got : Uncaught SoapFault exception: [1083] Coupon is not valid if i try the coupon code in the front end there is no problem. Is there anyone who have ever used this API part with success ?
Thanks.
This error comes from Mage_Checkout_Model_Cart_Coupon_Api::_applyCoupon()
if ($couponCode) {
if (!$couponCode == $quote->getCouponCode()) {
$this->_fault('coupon_code_is_not_valid');
}
}
This looks like it could be a bug, instead it should be if ($couponCode != $quote->getCouponCode()) { but I'm not certain.
It could be that the cart (quote) you're trying to apply the coupon to isn't valid, i.e. doesn't have the qualifying items it needs to receive the coupon. Are you sure $shoppingCartId correctly matches the expected quote in Magento's sales_flat_quote table?
I noticed that the error is in this excerpt:
try {
$quote->getShippingAddress()->setCollectShippingRates(true);
$quote->setCouponCode(strlen($couponCode) ? $couponCode : '')
->collectTotals()
->save();
} catch (Exception $e) {
$this->_fault("cannot_apply_coupon_code", $e->getMessage());
}
In this specific line: ->collectTotals() By removing this stretch , not of error , but not applied the coupon.
After debugging 2-3 hour on API, I have solved this error at my-end. Check below code which i have used in Coupon API.
<?php
$mage_url = 'http://yoursiteurl/api/soap?wsdl';
$mage_user= "API_User"; //webservice user login
$mage_api_key = "API_PASSWORD"; //webservice user pass
$client = new SoapClient($mage_url);
$couponCode = 'couponCode'; // a coupon to be apply
$shoppingCartId = '35'; // a cart Id which i have put test id
$sessionId = $client->login($mage_user, $mage_api_key);
$result = $client->call($sessionId,'cart_coupon.add',array($shoppingCartId,$couponCode));
print_r($result);
?>
The above code gives error that "Uncaught SoapFault exception: [1083] Coupon is not valid". When i debugg the core code i came to know that magento cart.create API insert wrong store id in sales_flat_quote table. I have changed the store id value in sales_flat_quote table manually and again run the Coupon API and after that it works perfectly. So here is the my solution. When you create cart id just run the below update query to change the store id.
<?php
$shoppingCartId = $soap->call( $sessionId, 'cart.create');
$mageFilename = '../app/Mage.php';
require_once $mageFilename;
umask(0);
Mage::app();
$db_write1 = Mage::getSingleton('core/resource')->getConnection('core_write');
$updateQue = "update sales_flat_quote set store_id='1' where entity_id ='".$shoppingCartId."'";
$db_write1->query($updateQue);
// Now run the Coupon API here
?>
Code taken from here : http://chandreshrana.blogspot.in/2015/11/uncaught-soapfault-exception-1083.html
You do not need to write direct SQL to resolve this issue. Just specify store ID parameter in the API call. Example is below is the demo script to apply discount code using Magento SOAP APIs V2 :
/* Set Discount Code */
try
{
$result = $client->shoppingCartCouponAdd($session, $quoteId, 'test123',$storeId);
echo "<br>Apply discount code: ";
var_dump($result);
}
catch(Exception $ex)
{
echo "<br>Discount code Failed: " . $ex->getMessage();
}
To apply discount code, perform following steps :
$quoteId = $client->shoppingCartCreate($session,$storeId);
/* Set cart customer */
$guest = true;
if ($guest)
{
$customerData = array(
"firstname" => "testFirstname",
"lastname" => "testLastName",
"email" => "testEmail#mail.com",
"mode" => "guest",
"website_id" => "1"
);
}
else
{
$customer = array(
"customer_id" => '69301',
"website_id" => "1",
"group_id" => "1",
"store_id" => "1",
"mode" => "customer",
);
}
//Set cart customer (assign customer to quote)
$resultCustomerSet = $client->shoppingCartCustomerSet($session, $quoteId, $customerData,$storeId);
/* Set customer addresses Shipping and Billing */
$addresses = array(
array(
"mode" => "shipping",
"firstname" => "Ahsan",
"lastname" => "testLastname",
"company" => "testCompany",
"street" => "testStreet",
"city" => "Karachi",
"region" => "Sindh",
"postcode" => "7502",
"country_id" => "PK",
"telephone" => "0123456789",
"fax" => "0123456789",
"is_default_shipping" => 0,
"is_default_billing" => 0
),
array(
"mode" => "billing",
"firstname" => "Ahsan",
"lastname" => "testLastname",
"company" => "testCompany",
"street" => "testStreet",
"city" => "Karachi",
"region" => "Sindh",
"postcode" => "7502",
"country_id" => "PK",
"telephone" => "0123456789",
"fax" => "0123456789",
"is_default_shipping" => 0,
"is_default_billing" => 0
)
);
//Set cart customer address
$resultCustomerAddress = $client->shoppingCartCustomerAddresses($session, $quoteId, $addresses,$storeId);
/* Set payment method */
$responsePayment = $client->shoppingCartPaymentMethod($session, $quoteId, array(
'method' => 'cashondelivery',
),$storeId);
/* Set shipping method */
$setShipping = $client->shoppingCartShippingMethod($session, $quoteId, 'flatrate_flatrate',$storeId);
After all above apply discount code,
try
{
$result = $client->shoppingCartCouponAdd($session, $quoteId, 'test123',$storeId);
echo "<br>Apply discount code: ";
var_dump($result);
}
catch(Exception $ex)
{
echo "<br>Discount code Failed: " . $ex->getMessage();
}