PHP Youshido GraphQL issue with nested fields - php-7

I am using version v1.4.2.18. The library can be found here: https://github.com/Youshido/GraphQL
I am trying to accomplish the following:
query {
articleSummary(id:1) {
title,
body,
article {
id
}
}
}
I have an ArticleSummaryField.php:
class ArticleSummaryField extends AbstractField
{
public function build(FieldConfig $config)
{
$config->addArgument('id', new NonNullType(new StringType()));
}
public function getType()
{
return new ArticleSummaryType();
}
public function resolve($value, array $args, ResolveInfo $info)
{
return [
'title' => 'test title',
'body' => 'test body',
'article' => $args['id']
];
}
}
Then the ArticleSummaryType.php:
class ArticleSummaryType extends AbstractObjectType
{
public function build($config)
{
$config
->addField('title', new StringType());
->addField('body', new StringType());
->addField('article', new ArticleField());
}
}
Then the ArticleField.php has the getType method return the ArticleType which has the id field.
However what i am getting is an error:
Fatal error: Uncaught Error: Call to undefined method ArticleField::getNullableType() in .../vendor/youshido/graphql/src/Execution/Processor.php on line 135
What seems to be happening is that when $targetField->getType() on line 135 in src/Execution/Processor.php is called its returning the ArticleField class, not the ArticleType class.
I would expect that to return the class as declared in the 'getType' method on the ArticleField class.
Am i going about this wrong for nesting fields? Or is there a bug in the library?

To accomplish this you only pass the Field class as the first argument.
class ArticleSummaryType extends AbstractObjectType
{
public function build($config)
{
$config
->addField('title', new StringType());
->addField('body', new StringType());
->addField(new ArticleField());
}
}
Then in the field class you can override getName to set the name for the field as needed or it will use the class name as the field name.

Related

What pattern to use to generate UUID when storing a model in Laravel 9?

I want to use a UUID as a part of a URL:
https://example.com/books/fiction/don-quixote-37d8d0a6-692a-11ed-9022-0242ac120002
Where in my controller should I generate this UUD?
class BookController extends Controller
{
public function store(BookStoreRequest $request)
{
$this->authorize('create', Book::class);
$validated = $request->validated();
$validated['identifier'] = (string) Str::uuid();
$book = Book::create($validated);
}
}
Above results in the error:
SQLSTATE Field 'identifier' doesn't have a default value
This assume that the value for identifier wasn't passed correctly?
Solution is to add a boot function to the model:
public static function boot()
{
parent::boot();
static::creating(function ($model) {
$model->identifier = Str::uuid();
});
}

AutoMapper - Map destination object's property that does not exist in source object

I'm casting class Process to ProcessDTO. The ProcessDTO object have a property named ProcessSteps that does not exist in Process. I want the ProcessSteps-property to be casted to ProcessStepsDto. I'm using a global configuration for AutoMapper.
I have tried using
CreateMap<Process, ProcessDto>()
.ForMember(dest=>dest.Steps, opt => opt.MapFrom(s => Mapper.Map<ProcessStepDto>(s)));
But this is wrong..
public class Process
{
}
public class ProcessDto
{
//This property does not exists in source object and get's created on get. I want this to be cast to "ProcessStepDto[]"
public ProcessStep[] Steps
{
get
{
ProcessStepRepository repository = new ProcessStepRepository();
return repository.Select(x => x.ProcessId == this.Id && x.Active).OrderBy(x=>x.Position).ToArray();
}
}
}
public class ProcessStep
{
...
}
public class ProcessStepDto
{
...
}
UPDATE
After i use AutoMapper to mapp my object Process to ProcessDto i also want the property of Stepsto be mapped to ProcessStepsDto. Currently it stays as ProcessStep.
If you want to return ProcessDto with ProcessStepDto[], the ProcessDto should define the property with type ProcessStepDto[] instead of ProcessStep[].
public class ProcessDto
{
public ProcessStepDto[] Steps
{
get
{
ProcessStepRepository repository = new ProcessStepRepository();
return repository.Select(x => x.ProcessId == this.Id && x.Active).Select(s => new ProcessStepDto { PropertyInProcessStepDto = s.PropertyInProcessStep }).OrderBy(x=>x.Position).ToArray();
}
}
}

Laravel Testing Error

I just started with learning how to test within Laravel. I came across some problems though..
I'm testing my controller and want to check if a View has a variable assigned.
My controller code:
class PagesController extends \BaseController {
protected $post;
public function __construct(Post $post) {
$this->post = $post;
}
public function index() {
$posts = $this->post->all();
return View::make('hello', ['posts' => $posts]);
}
}
And my view contains a foreach loop to display all posts:
#foreach ($posts as $post)
{{post->id}}
#endforeach
Last but not least my test file:
class PostControllerTest extends TestCase {
public function __construct()
{
// We have no interest in testing Eloquent
$this->mock = Mockery::mock('Eloquent', 'Post');
}
public function tearDown()
{
Mockery::close();
}
public function testIndex() {
$this->mock->shouldReceive('all')->once()->andReturn('foo');
$this->app->instance('Post', $this->mock);
$this->call('GET', '/');
$this->assertViewHas('posts');
}
}
Now comes the problem, when I run "phpunit" the following error appears:
ErrorException: Invalid argument supplied for foreach()
Any ideas why phpunit returns this error?
Your problem is here:
$this->mock->shouldReceive('all')->once()->andReturn('foo');
$this->post->all() (which is what you're mocking) should return an array, and that's what your view expects. You're returning a string.
$this->mock->shouldReceive('all')->once()->andReturn(array('foo'));
should take care of the error you have, though you'll then get an error of the "Getting property of non-object" type.
You could do this:
$mockPost = new stdClass();
$mockPost->id = 1;
$this->mock->shouldReceive('all')->once()->andReturn(array($mockpost));
You should mock the view as well:
public function testIndex() {
$this->mock->shouldReceive('all')->once()->andReturn('foo');
$this->app->instance('Post', $this->mock);
View::shouldReceive('make')->with('hello', array('posts', 'foo'))->once();
$this->call('GET', '/');
}

phpunit: mock object not fooling php

I'm going through the zend tutorials and I am testing a class with a mock object with phpunit. When I pass a mock created from Zend\Db\TableGateway to my class, who's constructor expects a Zend\Db\TableGateway, I get an type error:
"...Argument 1 passed to Album\Model\AlbumTable::__construct() must be an instance of Zend\Db\TableGateway\TableGateway, instance of Mock_TableGateway_65b55cb0 given..."
Is this supposed happen? Are phpunit mock objects supposed to be able to "fool" the class?
Here is the real class:
class AlbumTable {
protected $tableGateway;
public function __construct(TableGateway $tableGateway) {
$this->tableGateway = $tableGateway;
}
public function fetchAll() {
$resultSet = $this->tableGateway->select();
return $resultSet;
}
public function getAlbum($id){
$id = (int) $id;
$rowset = $this->tableGateway->select(array('id' => $id));
$row = $rowset->current();
if(!$row) {
throw new \Exception("Couldn't find row: $id");
}
return $row;
}
public function saveAlbum(Album $album) {
$data = array(
'artist' => $album->artist,
'title' => $album->title,
);
$id = (int)$album->id;
if ($id == 0) {
$this->tableGateway->insert($data);
} else {
if ($this->getAlbum($id)) {
$this->tableGateway->update($data, array('id' => $id));
} else {
throw new \Exception('Form id does not exist');
}
}
}
public function deleteAlbum($id) {
$this->tableGateway->delete(array('id' => $id));
}
}
and the test:
class AlbumTableTest extends PHPUnit_Framework_TestCase {
public function testFetchAllReturnsAllAlbums() {
$resultSet = new ResultSet();
$mockTableGateway = $this->getMock('Zend\Db\TableGateway',
array('select'), array(), '', false);
$mockTableGateway->expects($this->once())
->method('select')
->with()
->will($this->returnValue($resultSet));
$albumTable = new AlbumTable($mockTableGateway);
$this->assertSame($resultSet, $albumTable->fechAll());
}
}
and the error:
Time: 102 ms, Memory: 5.00Mb
There was 1 error:
1) AlbumTest\Model\AlbumTableTest::testFetchAllReturnsAllAlbums
Argument 1 passed to Album\Model\AlbumTable::__construct() must be an instance of Zend\Db\TableGateway\TableGateway, instance of Mock_TableGateway_65b55cb0 given, called in C:\Users\MEEE\Google Drive\code\iis\www\CommunicationApp\module\Album\test\AlbumTest\Model\AlbumTableTest.php on line 20 and defined
C:\Users\MEEE\Google Drive\code\iis\www\CommunicationApp\module\Album\src\Album\Model\AlbumTable.php:9
C:\Users\MEEE\Google Drive\code\iis\www\CommunicationApp\module\Album\test\AlbumTest\Model\AlbumTableTest.php:20
FAILURES!
Tests: 4, Assertions: 9, Errors: 1.
You are not mocking the correct class. You are creating a mock of a Zend\Db\TableGateway and you need to actually mock Zend\Db\TableGateway\TableGateway
Change you test code to:
$mockTableGateway = $this->getMock('Zend\Db\TableGateway\TableGateway',
array('select'), array(), '', false);
Your mock was failing a type-hint because your not mocking the correct class.
Mock objects will extend the class that you are mocking, so they will be an instance of the class being mocked.

Cast route parameter in Nancy is always null

I have a Nancy module which uses a function which expects as parameters a string (a captured pattern from a route) and a method group. When trying to pass the parameter directly it will not compile as I "cannot use a method group as an argument to a dynamically dispatched operation".
I have created a second route which attempts to cast the dynamic to a string, but this always returns null.
using System;
using Nancy;
public class MyModule : NancyModule
{
public MyModule()
{
//Get["/path/{Name}/action"] = parameters =>
// {
// return MyMethod(parameters.Name, methodToBeCalled); // this does not compile
// };
Get["/path/{Name}/anotherAction"] = parameters =>
{
return MyMethod(parameters.Name as string, anotherMethodToBeCalled);
};
}
public Response MyMethod(string name, Func<int> doSomething)
{
doSomething();
return Response.AsText(string.Format("Hello {0}", name));
}
public int methodToBeCalled()
{
return -1;
}
public int anotherMethodToBeCalled()
{
return 1;
}
}
Tested with the following class in a separate project:
using System;
using Nancy;
using Nancy.Testing;
using NUnit.Framework;
[TestFixture]
public class MyModuleTest
{
Browser browser;
[SetUp]
public void SetUp()
{
browser = new Browser(with =>
{
with.Module<MyModule>();
with.EnableAutoRegistration();
});
}
[Test]
public void Can_Get_View()
{
// When
var result = browser.Get("/path/foobar/anotherAction", with => with.HttpRequest());
// Then
Assert.AreEqual(HttpStatusCode.OK, result.StatusCode);
Assert.AreEqual("Hello foobar", result.Body.AsString()); //fails as parameters.Name is always null when cast to a string
}
}
You can find the whole test over on github
I've had similar issues when using 'as' so I tend to use explicitly cast it:
return MyMethod((string)parameters.Name, anotherMethodToBeCalled);
Also I think there was a bug raised with the casing on parameters, but I think it's better to keep them lowercase:
Get["/path/{name}/anotherAction"]
(string)parameters.name
Your code works for me with upper case and lowercase, using the explicit cast.