Error creating session context via php sdk - protocol-buffers

The goal is to create a session context via the PHP V2 SDK like this:
$session = $this->contextsClient->sessionName($this->projectId, $this->sessionId);
$contextName = $this->contextsClient->contextName($this->projectId, $this->sessionId, 'test-context-name');
$context = new Context();
$context->setName($contextName);
$context->setLifespanCount(2);
$context->setParameters(["test-param-key" => "test-param-value"]);
return $this->contextsClient->createContext($session, $context);
The code works fine without the $context->setParameters(["test-param-key" => "test-param-value"]); part. I need to add parameters to the context though.
The error I get is:
Exception {#3554
#message: "Expect message.",
#file: "/home/vagrant/code/vendor/google/protobuf/php/src/Google/Protobuf/Internal/GPBUtil.php",
#line: 197,
}
I followed the errors trail and the problem is Google's code in line 197:
public static function checkMessage(&$var, $klass)
{
if (!$var instanceof $klass && !is_null($var)) {
throw new \Exception("Expect message.");
}
}
is trying to assert if the array passed to the setParameters function is an instance of \Google\Protobuf\Struct class in this snippet right here
public function setParameters($var)
{
GPBUtil::checkMessage($var, \Google\Protobuf\Struct::class);
$this->parameters = $var;
return $this;
}
I would be really glad if someone could help me. I spent a lot of hours trying to figure this out and nothing yet

As the error message states, the parameters need to be of type \Google\Protobuf\Struct. Also, each of the values need to be of type \Google\Protobuf\Value. For your particular example, you could do the following:
$paramValue = new \Google\Protobuf\Value();
$paramValue->setStringValue("test-param-value");
$parameters = new \Google\Protobuf\Struct();
$parameters->setFields(["test-param-key" => $paramValue]);
$context->setParameters($parameters);
You can look the implementation of these two classes here:
https://github.com/google/protobuf/blob/master/php/src/Google/Protobuf/Value.php
https://github.com/google/protobuf/blob/master/php/src/Google/Protobuf/Struct.php

Related

Handle Exception From Within Method

I am implementing payments for my website using the API of an external service (ie. the service of the payment provider).
Let's say the user clicks 'BUY', and then we go to my controller which says something along the lines of:
public function buyFunction() {
$result = $this->ExternalService->pay();
if ($result->success == true) {
return 'We are happy';
}
}
I have also created the aforementioned externalService which has the pay() method:
class ExternalService {
public function pay() {
response = //Do stuff with Guzzle to call the API to make the payment
return response;
}
}
Now, sometimes things go wrong.
Let's say the API returns an error - which means that it throws a GuzzleException - how do I handle that?
Ideally, if there is an error, I would like to log it and redirect the user to a page and tell him that something went wrong.
What I've tried
I have tried using a try/catch statement within the pay() function and using abort(500) but this doesn't allow me to redirect to the page I want to.
I have tried using a try/catch statement within the pay() function and using return redirect('/mypage') but this just returns a Redirect object to the controller, which then fails when it tries to call result->success
I have tried using number 2 but also adding a try/catch block to the controller method, but nothing changed.
In the end, I have found two solutions. In both, I use a try/catch block inside the pay() method. Then I either return 0; and check in the controller if (result == 0) or I use abort( redirect('/mypage') ); inside the try/catch block of the pay() method.
What is the right way to handle this?
How to use the try/catch blocks?
In my experience, avoid handling exceptions let them pass through and handle them accordingly with try catches. This is the most pragmatic approach. Alternatively you will end up checking result is correct in weird places, eg. if ($result) {...}. Just assume it went good, except if the exception is thrown. Bonus: never do Pokemon catches with Exception $e unless you specifically needs it!
class ExternalService {
public function pay() {
try {
response = $client->get(...);
} catch (BadResponseException $exception) {
Log::warning('This should not happen check payment api: ' . $exception->getMessage());
throw new PaymentException('Payment did not go through');
}
return response;
}
}
Assuming you have your own Exception.
class PaymentException extends HttpException
{
public function __construct(?\Exception $previous = null)
{
parent::__construct(Response::HTTP_BAD_REQUEST, 'Unexpected error processing the payment', $previous);
}
}
This enables you to handle the flow in a controller, where it would make sense to handle the redirect. Sometimes if the exception is very integral or common to the web app, it can also be handled by the exception handler instead.
class PaymentController {
public function pay(PaymentService $service) {
try {
$payment = $service->buyFunction();
} catch (PaymentException $exception) {
return redirect()->route('app.payment.error');
}
return view('app.payment.success', compact('payment'));
}
}

Zend Expressive nested application

I'm trying to use zend expressive nested application, so I'm following this blog post :
https://framework.zend.com/blog/2017-03-15-nested-middleware-in-expressive.html
The problem seems to be in the Middleware factory:
class CreateBookMiddlewareFactory
{
public function __invoke(ContainerInterface $container)
{
$nested = new Application(
$container->get(RouterInterface::class),
$container
);
$nested->pipe(AuthenticationMiddleware::class);
$nested->pipe(ContentValidationMiddleware::class);
$nested->pipe(BodyParamsMiddleware::class);
$nested->pipe(BookValidationMiddleware::class);
$nested->pipe(CreateBookMiddleware::class);
return $nested;
}
}
I don't get how CreateBookMiddleware could be added to the pipe here as we are in its Factory. So piping it will call the factory, create a new nested application, which will call the factory, which will create another nested application...
( ! ) Fatal error: Maximum function nesting level of '256' reached, aborting! in /var/www/project/vendor/zendframework/zend-stratigility/src/Next.php on line
158
Is there something I'm not getting right from this blog post?
You named the factory CreateBookMiddlewareFactory. And then inside __invoke you have $nested->pipe(CreateBookMiddleware::class);. It depends on your config, but usually CreateBookMiddlewareFactory would be the factory for CreateBookMiddleware. So it's stuck in a loop because it keeps creating itself.
As you have the exact same code as in the blogpost, I'm guessing it's an error in that blog post. I think it should have been like in the last delegator factory example: without the last $nested->pipe(CreateBookMiddleware::class);.
I've notified the author of the blog post.
Edit: The blog post is updated with this fix:
namespace Acme\Api;
use Acme\AuthenticationMiddleware;
use Acme\ContentNegotiationMiddleware;
use Psr\Container\ContainerInterface;
use Zend\Expressive\Application;
use Zend\Expressive\Helper\BodyParams\BodyParamsMiddleware;
use Zend\Expressive\Router\RouterInterface;
class CreateBookMiddlewareFactory
{
public function __invoke(ContainerInterface $container)
{
$nested = new Application(
$container->get(RouterInterface::class),
$container
);
$nested->pipe(AuthenticationMiddleware::class);
$nested->pipe(ContentValidationMiddleware::class);
$nested->pipe(BodyParamsMiddleware::class);
$nested->pipe(BookValidationMiddleware::class);
// If dependencies are needed, pull them from the container and pass
// them to the constructor:
$nested->pipe(new CreateBookMiddleware());
return $nested;
}
}
I accepted #xtreamwayz answer for the clarification. But here's how I made it work:
class CreateBookMiddlewareFactory
{
public function __invoke(ContainerInterface $container)
{
$nested = new Application(
$container->get(RouterInterface::class),
$container
);
$nested->pipe($container->get(AuthenticationMiddleware::class));
$nested->pipe($container->get(ContentValidationMiddleware::class));
$nested->pipe($container->get(BodyParamsMiddleware::class));
$nested->pipe($container->get(BookValidationMiddleware::class));
// instanciate the new class, so it will not call the factory again
$nested->pipe(new CreateBookMiddleware());
return $nested;
}
}

Laravel 4 Container Internal Workings

I've been studying the laravel 4 container to get more knowledge of the internals of laravel and to upgrade my own skills in writing better code.
However i'm failing to understand 3 similar pieces of code.
I'll use the smallest snippet to keep this question clean.
Similar questions can be found in links below. Although people have replied with correct answers, I'm not satisfied with simply 'Knowing how to use it, but not knowing how it all works inside'. So i really hope someone can give an explanation to all this.
Question 1
Question 2
<?php namespace Illuminate\Container; use Closure, ArrayAccess, ReflectionParameter;
class BindingResolutionException extends \Exception {}
class Container implements ArrayAccess {
/**
* Wrap a Closure such that it is shared.
*
* #param Closure $closure
* #return Closure
*/
public function share(Closure $closure)
{
return function($container) use ($closure)
{
// We'll simply declare a static variable within the Closures and if
// it has not been set we'll execute the given Closure to resolve
// the value and return it back to the consumers of the method.
static $object;
if (is_null($object))
{
$object = $closure($container);
}
return $object;
};
}
}
How does the share method know that the $container variable in that function is in fact an instance of Illuminate\Container? It isn't defined within the scope of that function.
Neither is it defined in the following example usecase (which wouldn't help anyway)
class AuthServiceProvider extends ServiceProvider{
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
$this->app['auth'] = $this->app->share(function($app)
{
// Once the authentication service has actually been requested by the developer
// we will set a variable in the application indicating such. This helps us
// know that we need to set any queued cookies in the after event later.
$app['auth.loaded'] = true;
return new AuthManager($app);
});
}
}
I'd expect a different implementation, so here comes
class MyContainer{
public function share(Closure $closure)
{
$container = $this;
return function() use ($closure, $container)
{
static $object;
if(is_null($object))
{
$object = $closure($container);
}
return $object;
};
}
}
$closure = function($container)
{
var_dump($container);
};
$container = new MyContainer();
call_user_func($container->share($closure));
//dumps an instance of MyContainer -> which is the wanted behaviour
$container = new Illuminate\Container\Container();
call_user_func($container->share($closure));
//Throws a warning AND a notice
//Warning: Missing argument 1 for Illuminate\Container\Container::Illuminate\Container\{closure}() in /Users/thomas/Sites/Troll/vendor/illuminate/container/Illuminate/Container/Container.php on line 128
//NOTICE: Notice: Undefined variable: container in /Users/thomas/Sites/Troll/vendor/illuminate/container/Illuminate/Container/Container.php on line 137
//and even worse the output of the var_dump is NULL
I have the same problem in understanding the extend and the bind method, which both have the same implementation of passing a none-existing parameter as a closure argument, but i cannot grasp how it is resolved to the container instance itself?
The return value of Container::share() is a function that takes one argument: the container itself. In order to call it externally, you'd have to do this:
$closure = function ($container) {
var_dump($container);
};
$container = new Illuminate\Container\Container();
call_user_func($container->share($closure), $container);
The reason for this is due to how service definitions work. The intended use of share is to wrap around a service definition.
Like this:
$container = new Illuminate\Container\Container();
$container['foo'] = $container->share(function ($container) { return new Foo(); });
When you access a service, like this:
var_dump($container['foo']);
It checks if the value is callable, and if it is, it will try to call it as a function. If you leave off the share, you will get a new Foo instance every time. The share memoizes the instance and returns the same one every time.
To re-iterate, the $container argument in the function returned from share is there because that's how service creation works. The service definition ("factory" function that you "set" on the container) is just a function that takes a container and returns the instance of the service it is creating.
Since offsetGet() it is expecting the definition to take a $container argument, that's what share returns.

laravel-3 core exception on new installation

I have a new installation of Laravel on a machine running ubuntu 12.04 LTS, Nginx, php-cgi, and Laravel v3.2.14
I am getting the following error.
Unhandled Exception
Message:
Undefined index: argv
Location:
DOCUMNET ROOT/laravel/core.php on line 218
EDIT:
I have managed to get a slightly more descriptive error by commenting out the error handling function in laravel //Error::shutdown(); on line 50 of DOCUMENT ROOT/laravel/laravel.php
Note that this line does not cause the error, it only seems to register a handler for the code that does.
Here is the error:
Unhandled Exception
Message:
A driver must be set before using the session.
Location:
DOCUMENT ROOT/laravel/session.php on line 109
Stack Trace:
DOCUMENT ROOT/laravel/session.php(150): Laravel\Session::instance()
DOCUMENT ROOT/laravel/laravel.php(195):
Laravel\Session::__callStatic('save', Array)
DOCUMENT ROOT/laravel/laravel.php(195): Laravel\Session::save()
DOCUMENT ROOT/public/index.php(34): require('/mona/developme...')
{main}
These are the code blocks causing the error:
if (Config::get('session.driver') !== '')
{
Session::save();
}
Which calls this function in DOCUMENT ROOT/laravel/session.php
public static function __callStatic($method, $parameters)
{
return call_user_func_array(array(static::instance(), $method), $parameters);
}
$method contains the string 'save', and $parameters is an empty array.
Laravel thinks that it is being accessed by cli if you are using php-cgi.
The solution is Modifying DOCUMENT ROOT/laravel/request.php like this:
public static function cli()
{
// This is a hack to make laravel work with fast-cgi
// Added by David - 03/27/13
if(!array_key_exists('argv', $_SERVER)) return false;
return defined('STDIN') || (substr(PHP_SAPI, 0, 3) == 'cgi' && getenv('TERM'));
}
Checking if the $_SERVER super global has the 'argv' key before returning false permits cli to continue to function while using php-cgi.
Inspired by your answer, this worked for me:
public static function cli()
{
if(array_key_exists('HTTP_HOST', $_SERVER)) return false;
return defined('STDIN') || (substr(PHP_SAPI, 0, 3) == 'cgi' && getenv('TERM'));
}

Should a method should be throwing an exception to the Unit Test?

I have a simple method for sending emails:
public void notifyEmail(string messageSubject, string messageBody)
{
MailMessage message = new MailMessage(from, to);
message.Subject = messageSubject;
message.Body = messageBody;
SmtpClient client = new SmtpClient(smtp_client);
client.Send(message);
message.Dispose();//release everything related
}
And a unit test (I'm learning):
[TestMethod()]
public void notifyEmailTest()
{
eMail target = new eMail("TEST Subject","TEST Body"); // TODO: Initialize to an appropriate value
bool testSent = true;
try
{
target.notifyEmail();
}
catch (Exception)
{
testSent = false;
}
Assert.IsTrue(testSent);
}
I deliberately set the smtp_client variable value to something invalid.
Running the code in my project results in an error.
Running the test method results in a Pass. Should my test or method be structured differently so that errors will fail the test?
I always do everything I can to avoid putting try-catch clauses on my unit tests. Instead try using the ExpectedException attribute (the attribute is the same for NUnit and MSTest) and set the type to the exception you are expecting i.e.
[TestMethod]
[ExpectedException(typeof(NetworkException))]
public void ShouldThrowNetworkExceptionIfSmtpServerIsInvalid)
{
//... test code here.
}
Another approach that I have used is to create a static class with an AssertExpectedException method since sometimes a method can throw the same type of exception for different reasons and the only way to know for sure if the accurate message is being returned is with custom code since the attribute does not assert the message the thrown exception is returning.
Hope this helps.
Regards.
If you expect that target.notifyEmail() should be throwing an exception, then that's what you should be testing for. If you were using NUnit you could use Assert.Throws<T>, e.g.
[Test]
public void notifyEmailTestFails()
{
// TODO: Initialize to an appropriate value
eMail target = new eMail("TEST Subject","TEST Body");
Assert.Throws<InvalidOperationException>(target.notifyEmail());
}
However, now I see you're using VSUnit you should be using [ExpectedException(typeof(...))]
as mentioned in other answers.
In general you should have separate tests for success, failure, and for exception conditions.
The way I normally do this is to decorate the test with ExpectedException (
http://msdn.microsoft.com/en-us/library/microsoft.visualstudio.testtools.unittesting.expectedexceptionattribute(v=vs.80).aspx)
. But you want to catch something MUCH less generic than "Exception."
If you don't want to use expected exception, then instead of:
bool testSent = true;
try
{
target.notifyEmail();
}
catch (Exception)
{
testSent = false;
}
Assert.IsTrue(testSent);
You can be a little less verbose:
try{
target.notifyEmail();
Assert.Fail("Expected an exception here");
}
catch (SmtpException){
}
I would highly recommend you to try the FluenAssertions:
http://fluentassertions.codeplex.com/
They are simple awesome and Elegant
And they let you check the exception message (You can not do that with the ExpectedException attribute)
Example:
using FluentAssertions;
[TestMethod]
public void notifyEmailTest()
{
eMail target = new eMail("TEST Subject","TEST Body"); // TODO: Initialize to an appropriate value
target.Invoking(x => x.notifyEmail())
.ShouldThrow<YourExcpectedException>()
.WithMessage("Your expected message", FluentAssertions.Assertions.ComparisonMode.Substring);
}

Resources