I am pulling order information from an external API and saving the entire JSON response into a documents table.
I obviously do not want to call this API when testing so I have setup a factory, but I am struggling with how to pull in these JSON stubs. For example:
// factory
$factory->define(App\OrderDocument::class, function (Faker $faker) {
return [
'document_id' => $faker->uuid,
'status' => $faker->randomElement(['open', 'partial', 'processed', 'updating']),
'document' => $faker->text
];
});
// Currently using it like...
foreach ($this->fullDocuments() as $id => $document){
$a = factory(\App\OrderDocument::class)->create([
'document_id' => $id,
'status' => 'full'
]);
$a->setRawAttributes(['document' => $document]);
$a->save();
}
where $this->fullDocuments() contains an array of the document_id and raw JSON response from the API.
I know a better way to do this is to factory the entire JSON document. The JSON contains about 500 lines so it would be very time consuming but also I do not own this data, so I assume I should not be trying to fake it.
Within my test, I would prefer to do something like the below, but am not sure how.
factory(OrderDocument::class, 10)->create([
'document_id' => $this->getDocumentId($i++),
'document' => $this->getDocumentStub($i++),
]);
Related
UPDATE:
As it turns out, I need to enable this setting for data to show up, and using tabs is the correct thing to do.
When an envelope is sent, write the initial value of the field for all recipients
=================================================================
Not sure why this one is not mentioned in API properly ... but how does one go about filling template's custom data label with template?
So, I create a template like this:
$envelope_definition = new EnvelopeDefinition([
'status' => 'sent',
'template_id' => $args['template_id'],
]);
then I create a signer:
$signer = new TemplateRole([
'email' => $args['signer_email'],
'name' => $args['signer_name'],
'role_name' => 'signer',
]);
Here is where the disconnect happened, where do I add a pre-defined template value? I tried two things so far:
1. Add tabs to $signer like so, but by doing so, it ereases all field value in the final document,
new Tabs([
"text_tabs" => [
new Text([
"tab_label" => "price",
"value" => "123456789",
]),
],
]),
Call $envelope_definition->setCustomFields() , like this :
$envelope_definition->setCustomFields(new CustomFields([
'text_custom_fields' => [
'price' => new Text([
'tab_label' => 'price',
'custom_tab_id' => 'price',
'value' => '123456789',
]),
],
]));
It will throw me a C# error, which I don't understand at all:
Error while requesting server, received a non successful HTTP code [400] with response Body: O:8:\"stdClass\":2:{s:9:\"errorCode\";s:20:\"INVALID_REQUEST_BODY\";s:7:\"message\";s:718:\"The request body is missing or improperly formatted. Cannot deserialize the current JSON object (e.g. {\"name\":\"value\"}) into type 'System.Collections.Generic.List`1[API_REST.Models.v2_1.textCustomField]' because the type requires a JSON array (e.g. [1,2,3]) to deserialize correctly.\r\nTo fix this error either change the JSON to a JSON array (e.g. [1,2,3]) or change the deserialized type so that it is a normal .NET type (e.g. not a primitive type like integer, not a collection type like an array or List<T>) that can be deserialized from a JSON object. JsonObjectAttribute can also be added to the type to force it to deserialize from a JSON object.\r\nPath 'customFields.textCustomFields.price', line 1, position 45.\";}"
API docs seems to be focusing on creating template and values adhoc ... anyone have something that works? Thanks a lot!
You can find valid PHP example here which shows how to prepopulate template tab values while creating an envelope.
You didn't follow the example correctly.
You didn't create a \DocuSign\eSign\Model\TextCustomField object.
Here it is from Amit's link:
# Create an envelope custom field to save the our application's
# data about the envelope
$custom_field = new \DocuSign\eSign\Model\TextCustomField([
'name' => 'app metadata item',
'required' => 'false',
'show' => 'true', # Yes, include in the CoC
'value' => '1234567']);
$custom_fields = new \DocuSign\eSign\Model\CustomFields([
'text_custom_fields' => [$custom_field]]);
$envelope_definition->setCustomFields($custom_fields);
I have this nested relation im abit unsure how i assertJson the response within the phpunit test.
FilmController
public function show(string $id)
{
$film = Film::with([
'account.user:id,account_id,location_id,name',
'account.user.location:id,city'
])->findOrFail($id);
}
FilmControllerTest
public function getFilmTest()
{
$film = factory(Film::class)->create();
$response = $this->json('GET', '/film/' . $film->id)
->assertStatus(200);
$response
->assertExactJson([
'id' => $film->id,
'description' => $film->description,
'account' => $film->account->toArray(),
'account.user' => $film->account->user->toArray(),
'account.user.location' => $film->account->user->location->toArray()
]);
}
Obviously this isnt working because its returning every column for the user im a little unfamiliar with how you test nested relations with the code you need so im unsure with a toArray can anyone help out?
Testing is a place where you throw DRY (don't repeat yourself) out and replace it with hard coded solutions. Why? simply, you want the test to always produce the same results and not be bound up on model logic, clever methods or similar. Read this amazing article.
Simply hard code the structure you expect to see. If you changed anything in your model to array approach, the test would still pass even thou your name was not in the response. Because you use the same approach for transformation as testing. I have tested a lot of Laravel apps by now and this is the approach i prefers.
$account = $film->account;
$user = $account->user;
$location = $user->location;
$response->assertExactJson([
'description' => $film->description,
'account' => [
'name' => $account->name,
'user' => [
'name' => $user->name,
'location' => [
'city' => $location->city,
],
],
],
]);
Don't test id's the database will handle those and is kinda redundant to test. If you want to check these things i would rather go with assertJsonStructure(), which does not assert the data but checks the JSON keys are properly set. I think it is fair to include both, just always check the JSON structure first as it would likely be the easiest to pass.
$response->assertJsonStructure([
'id',
'description',
'account' => [
'id',
'name',
'user' => [
'id',
'name',
'location' => [
'id',
'city',
],
],
],
]);
I'm learning Laravel and have created a public endpoint where I want to output only certain information of some comments if a user is not authenticated from a GET request.
I have managed to filter out the comments based on whether or not they are approved. I now want to filter out the data that is returned. I have attached a screenshot of what is currently returned.
Ideally, I only want to return the id, name and the body in the json. How can I go about this? I tried the pluck() method which did not give the desired results. Any pointers would be greatly appreciated
public function index(Request $request)
{
if (Auth::guard('api')->check()) {
return Comment::all();
} else {
$comments = Comment::where('approved', 1)->get();
return $comments->pluck('id','name','body');
}
}
To select the particular columns, you can pass columns name to get as
$comments = Comment::where('approved', 1) -> get(['id','name','body']);
You can use a transformer to map the incoming data to a sensible output based on the auth state. The following example comes from the Fractal lib:
<?php
use Acme\Model\Book;
use League\Fractal;
$books = Book::all();
$resource = new Fractal\Resource\Collection($books, function(Book $book) {
return [
'id' => (int) $book->id,
'title' => $book->title,
'year' => $book->yr,
'author' => [
'name' => $book->author_name,
'email' => $book->author_email,
],
'links' => [
[
'rel' => 'self',
'uri' => '/books/'.$book->id,
]
]
];
});
Ideally, you would create 2 classes that extend from Transformer and pass the correct one to the output.
If you want to pass the result as json respose
$comments = Comment::where('approved', 1)->pluck('id','name','body')->toArray();
return Response::json($comments);
If you want to pass the result as to blade
$comments = Comment::where('approved', 1)->pluck('id','name','body')->toArray();
return view('your_blade_name')->with('comments',$comments);
I'm building an application with AngularJS that talks to a REST API developed in Laravel 4.
I've made a good start so far by having each request send the relevant Access-Control headers that allow Angular to talk to the API, and I've also built a UserController resource:
Route::resource('users', 'UserController');
However now I seem to be facing a wall. For the learning opportunities, I'd like to build this REST API properly using good standards, and I'm trying to work out how to do the responses.
I know I want to return JSON from the API and as far as I know I should wrap every response in a data field, and I also realize I can do that using something like this at the bottom of my controller actions:
return Response::json(array(
'data' => array(
'users' => array(
array(
'username' => 'Bob',
'email' => 'bob#gmail.com'
)
)
),
'status' => 200,
'success' => true
),
200
);
However, I don't want to repeat that kind of logic in every controller action in my application, so I'm wondering how I can make that code more DRY so that at the end of my controller I can just do something like this:
return array(
'users' => array(
array(
'username' => 'Bob',
'email' => 'bob#gmail.com'
)
)
);
and somewhere else in my application I can do the Response::json stuff. Any ideas?
So like you said, you want to return a JSON response, for example:
return Response::json(
array(
'status' => 'success',
'pages' => $modelData->toArray()
),
200
);
So, instead of repeating this everytime, you could just make a createResponse...() function, perhaps in a base controller class, that you can call in all your controllers. A response failed function could look something like:
function createResponseFailed() {
return Response::json([
'error' => [
'message' => 'Some failed message'
]
], 404);
}
That is just a simple example, but illustrates the point. Hope it helps!
I have a little REST API in my project. And ofcourse i use json as my return data to work with.
I am using symfony in the backend and angularJs in the frontend. At the moment i convert my entity to json by looping true my result and filling an data array to return as json.
EXAMPLE:
public function getAction($id)
{
$em = $this->getDoctrine()->getManager();
$warehouseId = $this->get('session')->get('warehouse');
$warehouse = $em->getRepository('BubbleMainBundle:Warehouse')->find($warehouseId);
$trip = $em->getRepository('BubbleMainBundle:Trip')->find($id);
$data = array(
'id' => $trip->getId(),
'driver' => $trip->getDriver(),
'status' => $trip->getStatus(),
'date' => $trip->getPlanningDate()->format('Y-m-d')
);
if ( count($trip->getStops()) > 0 ) {
foreach($trip->getStops() as $stop)
{
$data['assignedStops'][] = array(
'id' => $stop->getId(),
'status' => $stop->getStatus(),
'date' => $stop->getDeliveryDate()->format('Y-m-d'),
'sort' => $stop->getSort(),
'company' => array(
'name' => $stop->getToCompany()->getName(),
'lat' => $stop->getToCompany()->getLat(),
'lng' => $stop->getToCompany()->getLng(),
'address' => $stop->getToCompany()->getAddress(),
'zip' => $stop->getToCompany()->getZip()
),
);
}
} else {
$data['assignedStops'][] = '';
}
$response = new jsonResponse();
$response->setData($data);
return $response;
}
This works. But sometimes i have have (google chrome timeline) waiting responses of 6 seconds for just a simple query and json response.
Is looping true the entity to much? Or do i need another approach for converting my entities to json format?
thx anthony,
If you are using PHP 5.4 or above then considering using the JsonSerializable interface with your entities:
http://www.php.net/manual/en/class.jsonserializable.php
This will allow you to control how your entities are converted to JSON which will allow you to call json_encode directly on your entities without having to loop through and convert them to arrays.
As for the performance issue you'd need to profile your script to find out where the performance bottleneck is. From looking at your code one potential issue that you might want to look into is to make sure you are fetching all the data in your original query (i.e stops and companies) and you are not executing additional queries in the foreach loop to fetch the missing stop and company data.
I recommend you (since you are using Symfony2 as a backend and you need an API) to definitely try out this bundle... It's easy to use and to setup and as an extra you can also generate a nice documentation for it.
It will speed up your development and code.