How can I assert an instance of a ResourceCollection in Laravel? - laravel

I am working with a feature test and it's returning data correctly; all things are coming back correctly; and I'm at the final portion of my test.
I am struggling to assert that I'm getting back a ResourceCollection:
$this->assertInstanceOf(ResourceCollection::class, $response);
Here is the portion of my test:
MyFeature.php
...
$http->assertStatus(200)
->assertJsonStructure([
'data' => [
'*' => [
'type', 'id', 'attributes' => [
'foo', 'bar', 'baz',
],
],
],
'links' => [
'first', 'last', 'prev', 'next',
],
'meta' => [
'current_page', 'from', 'last_page', 'path', 'per_page', 'to', 'total',
],
]);
// Everything is great up to this point...
$this->assertInstanceOf(ResourceCollection::class, $response);
The error I get back is:
Failed asserting that stdClass Object (...) is an instance of class "Illuminate\Http\Resources\Json\ResourceCollection".
I'm not sure what I should be asserting in this case. I am getting back a resource collection, what should I be using instead? Thank you for any suggestions!
EDIT
Thank you #mare96! Your suggestion lead me to another approach that seemed to work. Which is great but I'm not too sure I really understand why...
Here's my full test (including my final assertion):
public function mytest() {
$user = factory(User::class)->create();
$foo = factory(Foo::class)->create();
$http = $this->actingAs($user, 'api')
->postJson('api/v1/foo', $foo);
$http->assertStatus(200)
->assertJsonStructure([
'data' => [
'*' => [
'type', 'id', 'attributes' => [
'foo', 'bar', 'baz'
],
],
],
'links' => [
'first', 'last', 'prev', 'next',
],
'meta' => [
'current_page', 'from', 'last_page', 'path', 'per_page', 'to', 'total',
],
]);
$this->assertInstanceOf(Collection::class, $http->getOriginalContent());
}

As I said in the comment above, your content will be the instance of Collection.
You can do it like that:
$this->assertInstanceOf(Collection::class, $http->getOriginalContent());
So, you can try to debug, to make it clearer, like this: Do dd($http); you should get an instance of Illuminate\Foundation\Testing\TestResponse not the same when you do $http->dump(); right?
So you need to assert an instance of just content, not the whole response.
I hope at least I helped a little.

Related

yii2 pagecahe array dependecny

I am implementing page cache for one of my page. For depenedency, I have to check an array, which can be either exist or not. Possible array keys cane ,
usersearch['id'], usersearch['name'], usersearch['phone]. I have to add dependency for any change in these values as well.
Also, I have to clear cache for any update or add in user table.
Is there any possible solution for this.?
Thanks in advance
You can use variations
public function behaviors(){
$usersearch = Yii::$app->requst->get('usersearch');
return [
[
'class' => 'yii\filters\PageCache',
'only' => ['index'],
'duration' => 60,
'variations' => [
'YOUR_DYNAMIC_VALUE1','YOUR_DYNAMIC_VALUE2'
],
'dependency' => [
'class' => 'yii\caching\DbDependency',
'sql' => 'SELECT COUNT(*) FROM post',
],
],
];
}
Ref link
IN YOUR CASE ,you can use
'variations' => \Yii::$app->requst->get('usersearch')??[],
or
'variations' => [
\/Yii::$app->requst->get('usersearch')['id'] ?? '',
\Yii::$app->requst->get('usersearch')['name'] ?? '',
\Yii::$app->requst->get('usersearch')['phone'] ?? '',
]
You could use a yii\caching\FileCache component with the following configuration.
Firstly, you set the cache in the init function of your controller:
Yii::$app->setComponents([
'yourCacheName' => [
'class' => \yii\caching\FileCache::class,
'defaultDuration' => 1800, //cache duration in seconds
'keyPrefix' => Yii::$app->getSession()->getId(). '_'
]
]);
Here, the parameter keyPrefix is set so that it is linked to the session ID. Thus, the visitors do not see each other's cached page. If the content is static and equal, regardless of the user or the session, this parameter can be removed.
In the view that must be cached you can call the beginCache function and the dependency as follows:
$this->beginCache('cache-id', [
'cache' => Yii::$app->yourCacheName, // the name of the component as set before
'variations' => [
$usersearch['id'] ?? '',
$usersearch['name'] ?? '',
$usersearch['phone'] ?? '',
],
'dependency' => [
'class' => \yii\caching\DbDependency::class,
'sql' => 'SELECT count(*) FROM your_user_table'
]
]);
// your view
$this->endCache();

Laravel Elasticsearch JSON Mapping Issue

I'm currently using Laravel v7.2, have the babenkoivan/scout-elasticsearch-driver installed (4.2) and am using AWS Elasticsearch 7.1. I have several tables mapped in my application that are working fine but am having issues with a nested mapping that was previously working and is now broken.
I'm saving data into a table and having that table data copied into AWS Elasticsearch. I'm using MySQL 5.6 so I am using a TEXT column to store JSON data. Data in the table looks as follows:
'id' => 1,
'results' => [{"finish":1,"other_id":1,"other_data":1}]
I have my model setup with the following mapping:
protected $mapping = [
'properties' => [
'results' => [
'type' => 'nested',
'properties' => [
'finish' => [
'type' => 'integer'
],
'other_id' => [
'type' => 'integer'
],
'other_data' => [
'type' => 'integer'
]
]
],
]
];
And if it's of any use, the toSearchableArray:
public function toSearchableArray()
{
$array = [
'id' => $this->id,
'results' => $this->results
];
return $array;
}
I have no problem creating this index and it worked up until about a couple of months ago. I don't know exactly when, as it wasn't a high priority item and may have occurred around an AWS ES update but not sure why this in particular would break. I receive the following error now:
{"error":{"root_cause":[{"type":"mapper_parsing_exception","reason":"object mapping for [results] tried to parse field [results] as object, but found a concrete value"}],"type":"mapper_parsing_exception","reason":"object mapping for [results] tried to parse field [results] as object, but found a concrete value"},"status":400}
I've tried also storing the data in the table as such, thinking it was breaking due to the potential array, but it was to no avail:
'id' => 1,
'results' => {"finish":1,"other_id":1,"other_data":1}
I'm at a loss for what else to try to get this working again.
EDIT: Here is the entire model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use ScoutElastic\Searchable;
class ResultsModel extends Model
{
use Searchable;
protected $indexConfigurator = \App\MyIndexConfiguratorResults::class;
protected $searchRules = [
//
];
protected $mapping = [
'properties' => [
'results' => [
'type' => 'nested',
'properties' => [
'finish' => [
'type' => 'integer'
],
'other_id' => [
'type' => 'integer'
],
'other_data' => [
'type' => 'integer'
]
]
],
]
];
public function searchableAs()
{
return 'results_index';
}
public function toSearchableArray()
{
$array = [
'id' => $this->id,
'results' => $this->results
];
return $array;
}
/**
* The database table used by the model.
*
* #var string
*/
protected $table = 'results_table';
}
Here is the \App\MyIndexConfiguratorResults::class
<?php
namespace App;
use ScoutElastic\IndexConfigurator;
use ScoutElastic\Migratable;
class MyIndexConfiguratorResults extends IndexConfigurator
{
use Migratable;
protected $name = "results_index";
/**
* #var array
*/
protected $settings = [
//
];
}
This is all that is needed to have Laravel automatically update AWS ES each time the table is updated. For the initial load, I would SSH in and run the following command to have it create the index. This, as well as elastic:migrate and any update/insert into the table produces the mapping error.
php artisan elastic:create-index results_index
Finally figured this out so will share the solution for anyone that runs into this. Turns out to be a fairly simple fix, though I'm not sure how it even worked in the first place so that part is still baffling.
I created a brand new index and update the mappings accordingly to add "id" and remove the type "nested" from the "results" piece. (Adding the "nested" type was adding two "results" to the index - one that contained all my nested data, the other just being "object".)
protected $mapping = [
'properties' => [
'id' => [
'type' => 'integer'
],
'results' => [
'properties' => [
'finish' => [
'type' => 'integer'
],
'other_id' => [
'type' => 'integer'
],
'other_data' => [
'type' => 'integer'
]
]
],
]
];
Then I simply added json_decode to the toSearchableArray() function as so:
public function toSearchableArray()
{
$array = [
'id' => $this->id,
'results' => json_decode($this->results, true)
];
return $array;
}
Voila. It successfully created the index and imported the data in a manner with which I can query the nested object.
Reading through the docs, the type field seems to have been removed. Scroll down to 7.x to see. Also, it seems you need to delete the index and re-add it in order for the new map to work according to this page.

Laravel: How can I assertJson an array

I am creating a feature test for a Seminar. Everything is working great; I am trying to update my feature test to account for the seminar dates.
Each Seminar can have one or many dates, so I am saving these values as a json field:
// migration:
...
$table->json('dates');
...
Here is what my Seminar model looks like:
// Seminar.php
protected $casts = [
'dates' => 'array',
];
When saving the seminar, I am returning a json resource:
if ($seminar->save()) {
return response()->json(new SeminarResource($seminar), 200);
...
Using Postman, my Seminar looks a like this:
...
"capacity": 100,
"dates": [
"2020-10-15",
"2020-10-16"
],
...
So far so good!
In my test, I am testing that a seminar can be created.
$http->assertStatus(201)
->assertJson([
'type' => 'seminars',
'id' => (string)$response->id,
'attributes' => [
'dates' => $response->attributes->dates, // General error: 25 column index out of range
I've tried to convert the array to a string, or json_encode the value in the resource. I don't think that's the correct way since I am already casting the value as an array in the model.
How can I assert that my dates is returning an array?
+"dates": array:2 [
0 => "2020-10-15"
1 => "2020-10-16"
]
Thank you for your suggestions!
EDIT
When I dd($response->attributes->dates); this is what I'm getting (which is correct).
array:2 [
0 => "2020-10-15"
1 => "2020-10-16"
]
What I'm not sure is how to assert an array like that. Since I'm using faker to generate the date, I don't really know (or care) what the date is, just want to assert that it is in fact an array.
I've tried something like:
'dates' => ['*'],
However, that just adds another element to the array.
EDIT 2
If I make the array a string,
'dates' => json_encode($response->attributes->dates),
I'll get an error like this:
--- Expected
+++ Actual
## ##
- 'dates' => '["2020-10-15","2020-10-16"]',
+ 'dates' =>
+ array (
+ 0 => '2020-10-15',
+ 1 => '2020-10-16',
+ ),
In my database, the values are stored like this:
["2020-10-15","2020-10-16"]
My actual test looks like this:
$http->assertStatus(201)
->assertJsonStructure([
'type', 'id', 'attributes' => [
'name', 'venue', 'dates', 'description', 'created_at', 'updated_at',
],
])
->assertJson([
'type' => 'workshops',
'id' => (string)$response->id,
'attributes' => [
'name' => $response->attributes->name,
'venue' => $response->attributes->venue,
'dates' => $response->attributes->dates,
'description' => $response->attributes->description,
'created_at' => (string)$response->attributes->created_at,
'updated_at' => (string)$response->attributes->updated_at,
],
]);
$this->assertDatabaseHas('workshops', [
'id' => $response->id,
'name' => $response->attributes->name,
'venue' => $response->attributes->venue,
'dates' => $response->attributes->dates,
'description' => $response->attributes->description,
]);

Guzzle Post Null/Empty Values in Laravel

I've been trying to work with Guzzle and learn my way around it, but I'm a bit confused about using a request in conjunction with empty or null values.
For example:
$response = $client->request('POST',
'https://www.testsite.com/coep/public/api/donations', [
'form_params' => [
'client' => [
'web_id' => NULL,
'name' => 'Test Name',
'address1' => '123 E 45th Avenue',
'address2' => 'Ste. 1',
'city' => 'Nowhere',
'state' => 'CO',
'zip' => '80002'
],
'contact' => [],
'donation' => [
'status_id' => 1,
'amount' => $donation->amount,
'balance' => $donation->balance,
'date' => $donation->date,
'group_id' => $group->id,
],
]
]);
After running a test, I found out that 'web_id' completely disappears from my request if set to NULL. My question is how do I ensure that it is kept around on the request to work with my conditionals?
At this point, if I dd the $request->client, all I get back is everything but the web_id. Thanks!
I ran into this issue yesterday and your question is very well ranked on Google. Shame that it has no answer.
The problem here is that form_params uses http_build_query() under the hood and as stated in this user contributed note, null params are not present in the function's output.
I suggest that you pass this information via a JSON body (by using json as key instead of form_params) or via multipart (by using multipart instead of form_params).
Note: those 2 keys are available as constants, respectively GuzzleHttp\RequestOptions::JSON and GuzzleHttp\RequestOptions::MULTIPART
Try to define anything like form_params, headers or base_uri before creating a client, so:
// set options, data, params BEFORE...
$settings = [
'base_uri' => 'api.test',
'headers' => [
'Accept' => 'application/json',
'Authorization' => 'ajustneedatokenheredontworry'
],
'form_params' => [
'cash' => $request->cash,
'reason' => 'I have a good soul'
]
];
// ...THEN create your client,
$client = new GuzzleClient($settings);
// ...and finally check your response.
$response = $client->request('POST', 'donations');
If you check $request->all() at the controller function that you are calling, you should see that were sent successfully.
For those using laravel, use this:
Http::asJson()

Yii2 Restful API - add possibility to auth with session

In my Restful API project I use Bearer Token Authentication.
But now there is need to use Session Authentication in only one Controller to perform special actions with files. But I don't really understand how I should change behaviors method for that. What I have done,
'enableSession' => true
My behaviors method looks like:
public function behaviors() {
$behaviors = parent::behaviors();
unset($behaviors['authenticator']);
$behaviors['authenticator'] = [
'class' => HttpBearerAuth::className(),
'except' => ['options'],
];
$behaviors['access'] = [
'class' => AccessControl::className(),
'rules' => [
[
'allow' => true,
'actions' => [
'options',
],
],
[
'actions'=>['content'],
'allow' => true,
'roles' => ['admin'],
]
],
];
return $behaviors;
}
I think there should be some changes in authenticator settings, use something else except HttpBearerAuth or may be something else, please help

Resources