I have a Wordpress website with the WPGraphQL plugin installed and running some tests with Codeception to test the GraphQL queries. For a bit more context, I am using Codeception's REST module and loading Wordpress in the tests with wp-browser.
The following test to register a user is failing:
class RegisterCest {
public function seeCanRegister( ApiTester $I ) {
$I->send( "POST", "https://example.com/graphql", [
"query" => '
mutation registerUser( $input: RegisterUserInput! ) {
registerUser( input: $input ) {
user {
username
email
addressMain
addressSec
adminArea
country
city
firstName
lastName
postalCode
}
}
}
',
"variables" => [
"input" => [
"clientMutationId" => "registerUser",
"firstName" => "John",
"lastName" => "Smith",
"username" => "user#example.com",
"email" => "user#example.com",
"password" => "example",
"addressMain" => "Fake Street",
"addressSec" => "Apt #1",
"adminArea" => "New York",
"country" => "US",
"city" => "New York",
"postalCode" => 00000,
"planSlug" => null,
"subscribeToNewsletter" => true
]
]
]);
$I->seeResponseCodeIs( 200 );
$I->dontSeeResponseJsonMatchesJsonPath( "$.errors" );
}
}
The test fails because I get the following error in the response:
Expected type Boolean at value.subscribeToNewsletter; Boolean cannot
represent a non boolean value: 1"
Basically, what seems to happen is that the boolean true set as the value for subscribeToNewsletter is transformed into the string "1" before the query is executed, which causes the query to be invalid because in the GraphQL schema it is specified that subscribeToNewsletter is expected to be a boolean.
I do not get this error when running the query in the app; it's only coming up in this test. Can anyone think of a reason why?
As suggested by Arvin Jason Cabrera in the comments, the problem was I was not setting the Content-Type to application/json. In my case, I simply had to add the following line
$I->haveHttpHeader('Content-Type', 'application/json');
before sending the request.
Related
Laravel 8
I have seen a few of these questions, but the answers are either msising, not for php, or some weird hack.
I have a table in the database, mariadb, with the field type of LONGTEXT - equates to JSON field.
in my model I do:
protected $casts = [
'event_data' => 'array',
];
public function setEventDataAttribute($value) {
$this->attributes['event_data'] = json_encode($value);
}
The data going into the field is:
array:12 [
"start" => "2022-08-23T00:00:00+00:00"
"end" => "2022-08-23T00:00:00+00:00"
"all_day" => false
"unassigned" => true
"draft" => true
"title" => "ggggg"
"notes" => "test"
"active" => true
"schedule_calendar_id" => null
"jobcode_id" => 122723308
"customfields" => array:2 [
1782352 => "Dirty"
1782354 => "Vacant"
]
"color" => "#888888"
]
When I run json_encode($value) where $value is the above array, I get:
{
"start": "2022-08-23T00:00:00+00:00",
"end": "2022-08-23T00:00:00+00:00",
"all_day": false,
"unassigned": true,
"draft": true,
"title": "ggggg",
"notes": "test",
"active": true,
"schedule_calendar_id": null,
"jobcode_id": 122723308,
"customfields": {
"1782352": "Dirty",
"1782354": "Vacant"
},
"color": "#888888"
}
which according to every validator out there, this is valid JSON. How ever attempting to set this as the attribute into the field throws:
Malformed UTF-8 characters, possibly incorrectly encoded
I can, above the $this->attributes['event_data'] do:
dump(json_encode($value), json_decode(json_encode($value)));
And get the json object listed above and get a stdClass class object of the decoded json.
So my question is:
If the online JSON formatters are saying this is valid JSON, php has no issue encoding and decoding it - why can't laravel insert it? Is it the dates? they must be in ISO8601 Format.
What is going on? I have done this, json encoding like this, a thousand times with no issue.
The Wordpress REST API lets you validate a parameter against a regex if you provide one in the API route's schema:
register_rest_route( $this->namespace, "/posts", [
"methods" => "POST",
"permission_callback" => "__return_true",
"callback" => "create_post",
"args" => [
"title" => [
"type" => "string",
"description" => "The posts's title",
"required" => true,
"pattern" => '/^[A-Za-z0-9 ]+$/' // <-- Here's the pattern!
]
]
]);
The pattern I provided should accept all alphanumeric values and single spaces. Indeed, the pattern works if I test it in isolation:
preg_match( '/^[A-Za-z0-9 ]+$/', 'Title'); // Returns 1 as expected
preg_match( '/^[A-Za-z0-9 ]+$/', 'Test Title'); // Returns 1 as expected
preg_match( '/^[A-Za-z0-9 ]+$/', '123'); // Returns 1 as expected
preg_match( '/^[A-Za-z0-9 ]+$/', '&Title&'); // Returns 0 as expected
However, the pattern does not seem to work in the context of the Wordpress API! If I make a request to the /post route passing any of the above test values as the title, I always get the following error:
{
"code": "rest_invalid_param",
"message": "Invalid parameter(s): title",
"data": {
"status": 400,
"params": {
"title": "title does not match pattern /^[A-Za-z0-9 ]+$/."
},
"details": {
"title": {
"code": "rest_invalid_pattern",
"message": "title does not match pattern /^[A-Za-z0-9 ]+$/.",
"data": null
}
}
}
}
I've dug a bit deeper and discovered that internally Wordpress uses this function:
function rest_validate_json_schema_pattern( $pattern, $value ) {
$escaped_pattern = str_replace( '#', '\\#', $pattern );
return 1 === preg_match( '#' . $escaped_pattern . '#u', $value );
}
If I run my test values against this function, preg_match always returns false. Unfortunately I don't know the first thing about regexes so I don't understand what this function is doing. I've read the #u bit should be about unicode characters, but can't figure it out besides that. Can somebody please help?
(The Wordpress docs for using a pattern are here, but I can't work anything out of that either).
Got it. So apparently what's happening is that the rest_validate_json_schema_pattern function is adding the regex's delimiting characters, and since my regex already had delimiting characters, things were not working, so the solution is to provide the regex without any delimiting characters to begin with:
register_rest_route( $this->namespace, "/posts", [
"methods" => "POST",
"permission_callback" => "__return_true",
"callback" => "create_post",
"args" => [
"title" => [
"type" => "string",
"description" => "The posts's title",
"required" => true,
"pattern" => '^[A-Za-z0-9 ]+$' // <-- Regex without delimiting characters
]
]
]);
Confusing if you ask me, but it works!
For those who don't want to read the whole question:
I'm looking for the index in the API-Request (Zammad) to set a tag while creating a ticket.
Details:
I'm using PHP to make an API-Request to my server where Zammad is installed. The following shows the data i sent via curl:
json_encode([
"title" => $title,
"group_id" => 2,
"priority_id" => 2,
"category" => 'Some Category',
"state_id" => 1,
"type" => "Some Type",
"customer" => $userID,
"article" => [
"body" => $htmlBody,
"type" => "note",
"content_type" => "text/html",
],
"tag_list" => [ // <-- The question is about this segment
"The tag i want to add",
],
]);
After converting the data to JSON, im sending it via POST to http://<myServerIP>/api/v1/tickets
What I've tried so far:
I tried guessing the index of the tag at which i failed.
The first full example is shown above.
Secondly:
...
"tag_id" => 9, // I've check its the actual ID of the tag i want to add
And Finally:
...
"tag" => "The tag i want to add",
Needless to say that i didn't succeed. Sometimes i get an error id (im assuming its because the index doesn't exist [who would have thought that? :)]), sometimes i get nothing and Zammad just creates the ticket without the tag. What do i mean when i say sometimes? I refer my tries specified above.
What I've also tried:
Searching for some answer on the web. The thing that comes close to what i want is this. But i would rather create the ticket with the tag instead of making another request just to add the tag.
I've looked inside the code, its written in ruby. The index is 'tags' and needs to be sperated by ,.
Basicly:
json_encode([
"title" => $title,
"group_id" => 2,
"priority_id" => 2,
"category" => 'Some Category',
"state_id" => 1,
"type" => "Some Type",
"customer" => $userID,
"article" => [
"body" => $htmlBody,
"type" => "note",
"content_type" => "text/html",
],
"tags" => "tag1,tag2,tag3", // or simply "tags" => "tag1"
]);
It might help someone in the future ...
I have a page which allows users to query datasets and apply filters. They can also apply filters without querying with a string. To do so I'm attempting to use match_all with filters but getting the following error
"{"error":{"root_cause":[{"type":"parsing_exception","reason":"[match_all]
malformed query, expected [END_OBJECT] but found
[FIELD_NAME]","line":1,"col":26}],"type":"parsing_exception","reason":"[match_all]
malformed query, expected [END_OBJECT] but found
[FIELD_NAME]","line":1,"col":26},"status":400}",
This is an example of the search parameters that I'm building and sending to the elastic client.
[
"type" => "events"
"index" => "events"
"body" => [
"query" => [
"match_all" => {}
"bool" => [
"filter" => [
"range" => [
"start_date.date" => [
"gte" => "01/05/2019"
"lte" => "05/2019"
"format" => "dd/MM/yyyy||MM/yyyy"
]
]
]
]
]
"from" => 0
"size" => 30
]
]
I can't seem to figure out how to use both of them. Any pointers? Thank you.
You will need to wrap your query in a bool query like this:
"query": {
"bool" : {
"must" : {
"match_all": {}
},
"filter": {
"range" : { /* your filter here*/ }
}
}
}
Just wrap the bool and a must query around your match_all and it should work.
I don't know the exact PHP syntax but it should be something like this:
[
"type" => "events"
"index" => "events"
"body" => [
"query" => [
"bool" => [
"must" => [ "match_all" => {}]
"filter" => [
"range" => [
"start_date.date" => [
"gte" => "01/05/2019"
"lte" => "05/2019"
"format" => "dd/MM/yyyy||MM/yyyy"
]
]
]
]
]
"from" => 0
"size" => 30
]
]
For reference see the docs Elasticsearch Reference [7.0] » Query DSL » Compound queries » Bool Query, it contains an example like yours with match_all combined with filters.
I'm kind of perplexed with this issue. My code is really different so I expected different results. But I'm running form validations and trying to call the is_unique function. But it appears that it's adding my subdomain to the table name in the query. And for the life of me i can't figure out why.
I have a library class that sets up the controls it requires here
protected function settablecolumnattributes(){
$this->addcolumnattributes(
array(
"name" => "user_login",
"type" => "VARCHAR",
"length" => "255",
"default" => "NULL",
"collation" => "",
"attributes" => "",
"null" => "",
"index" => "",
"autoincrement" => "",
"comments" => "",
"width" => "",
"sortable" => "true",
"control" => array(
"type" => "textbox",
"label" => "User Login",
"id" => "",
"class" => "",
"placeholder" => "Enter a Username",
"validations" => "required|is_unique[blah.user_login]"
),
"visible_on_form" => true,
"visible_on_table" => true
)
);
}
then i am collecting all of the control validations here and outputting them.
public function set_object_form_validations(){
$fields = $this->getcolumnattributes();
foreach( $fields as $field ):
$visible_on_form = property_exists( $field, "visible_on_form" );
if( $visible_on_form !== false ):
if( $field->visible_on_form !== true ):
continue;
endif;
endif;
$this->CI->form_validation->set_rules($field->name, $field->control['label'], $field->control['validations']);
endforeach;
}
now when the validations encounters the is_unique i get this error
Error Number: 1146
Table '12385468.demo_blah' doesn't exist
SELECT * FROM `demo_blah` WHERE `user_login` = 'joe' LIMIT 1
Filename: libraries/Form_validation.php
Line Number: 1125
the testing url i am using is like this demo.example.com/admin/users/add
I've went into the form validations file and tried to trace this issue back but it just keeps going deeper and deeper into the CI functions. Every spot that $table is present just says blah, but you can see in the query it says demo_blah. I'm puzzled.
Figured this out after drilling through all of the codeigniter functions. Turns out there was a database prefix set inside config.php = $config['dbprefix']. I thought it was the subdomain originally because it said demo. but the prefix was set to demo also. There was actually nothing wrong with the query just my settings.