I'm trying to create Youtube live stream through my webpage via Youtube Data API. Whatever I tried, keep getting that error:
{
"error": {
"code": 400,
"message": "'{0}'",
"errors": [
{
"message": "'{0}'",
"domain": "youtube.part",
"reason": "unknownPart",
"location": "part",
"locationType": "parameter"
}
]
}
}
Unfortunately, this error doesn't explain anything, and I couldn't find anything to help me to solve it. I hope someone can explain what is going on here.
I put all relative files down below and added some comments.
web.php
Route::get('youtube/{task}', [YoutubeController::class, 'authenticate'])->name('youtube.authenticate');
Route::get('youtube/{task}/redirect', [YoutubeController::class, 'create'])->name('youtube.create');
YoutubeController.php
class YoutubeController extends Controller
{
private $youtube;
public function __construct(Request $request)
{
// like YoutubeStreamService or YoutubeUploadService
$this->youtube = new ("\App\Services\Youtube\Youtube" . ucfirst($request->route()->parameter('task')) . "Service");
}
public function authenticate($task)
{
return redirect()->away($this->youtube->authenticate($task));
}
public function create(Request $request, $task)
{
$this->youtube->create($request, $task);
}
}
I use an abstract class for authentication codes.
abstract class YoutubeAbstraction
{
// Called from the controller.
// Returns the url to google to authenticate the request.
public function authenticate($task)
{
return $this->client($task)->createAuthUrl();
}
// This code came from mostly Youtueb API documentation.
protected function client($task)
{
$scopes = [
'upload' => ['https://www.googleapis.com/auth/youtube.upload', 'https://www.googleapis.com/auth/youtube.force-ssl'],
'stream' => ['https://www.googleapis.com/auth/youtube.force-ssl']
][$task];
$client = new Google_Client();
$client->setApplicationName("MyApp");
$client->setScopes($scopes);
$client->setAuthConfig(base_path("client_secret_{$task}.json"));
$client->setAccessType('offline');
return $client;
}
abstract public function create($request, $task);
}
YoutubeStreamService.php
class YoutubeStreamService extends YoutubeAbstraction
{
// This code came from Youtube API documentation completely.
// It contains only the required fields and their hard-coded values.
public function create($request, $task)
{
$client = $this->client($task);
$client->setAccessToken($client->fetchAccessTokenWithAuthCode($request->code));
$service = new Google_Service_YouTube($client);
$liveBroadcast = new Google_Service_YouTube_LiveBroadcast();
$liveBroadcastSnippet = new Google_Service_YouTube_LiveBroadcastSnippet();
$liveBroadcastSnippet->setTitle('my title');
$liveBroadcastSnippet->setScheduledStartTime('2021-04-04T20:00:00.00+03:00');
$liveBroadcast->setSnippet($liveBroadcastSnippet);
$liveBroadcastStatus = new Google_Service_YouTube_LiveBroadcastStatus();
$liveBroadcastStatus->setPrivacyStatus('private');
$liveBroadcast->setStatus($liveBroadcastStatus);
// If I add dd($liveBroadcast) here, I see the object.
// So the error is thrown by the function down below.
$response = $service->liveBroadcasts->insert('', $liveBroadcast);
print_r($response);
}
}
As per the official specification, your call to the LiveBroadcasts.insert API endpoint has to include the request parameter:
part (string)
The part parameter serves two purposes in this operation. It identifies the properties that the write operation will set as well as the properties that the API response will include.
The part properties that you can include in the parameter value are id, snippet, contentDetails, and status.
In PHP, that requirement boils down to having your API call like the one below:
$response = $service->liveBroadcasts->insert(
'id,snippet,status', $liveBroadcast);
Related
I recently changed my DocuSign integration to use the JWT OAuth flow. To achieve this I have a few classes.
OAuth Client
<?php
namespace App\DocuSign;
use DocuSign\eSign\Client\ApiClient;
use DocuSign\eSign\Client\Auth\OAuth;
use DocuSign\eSign\Configuration;
use Exception;
use Illuminate\Support\Facades\Log;
/**
* Helper class to generate a DocuSign Client instance using JWT OAuth2.
*
* #see
*
*/
class OAuthClient
{
/**
* Create a new DocuSign API Client instance using JWT based OAuth2.
*/
public static function createApiClient()
{
$config = (new Configuration())->setHost(config('docusign.host'));
$oAuth = (new OAuth())->setOAuthBasePath(config('docusign.oauth_base_path'));
$apiClient = new ApiClient($config, $oAuth);
try {
$response = $apiClient->requestJWTUserToken(
config('docusign.integrator_key'),
config('docusign.user_id'),
config('docusign.private_key'),
'signature impersonation',
60
);
if ($response) {
$accessToken = $response[0]['access_token'];
$config->addDefaultHeader('Authorization', 'Bearer ' . $accessToken);
$apiClient = new ApiClient($config);
return $apiClient;
}
} catch (Exception $e) {
// If consent is required we just need to give the consent URL.
if (strpos($e->getMessage(), 'consent_required') !== false) {
$authorizationUrl = config('docusign.oauth_base_path') . '/oauth/auth?' . http_build_query([
'scope' => 'signature impersonation',
'redirect_uri' => config('docusign.redirect_url'),
'client_id' => config('docusign.integrator_key'),
'response_type' => 'code'
]);
Log::critical('Consent not given for DocuSign API', [
'authorization_url' => $authorizationUrl
]);
abort(500, 'Consent has not been given to use the DocuSign API');
}
throw $e;
}
}
}
Signature Client Service
<?php
namespace App\DocuSign;
use DocuSign\eSign\Api\EnvelopesApi;
use DocuSign\eSign\Client\ApiClient;
class SignatureClientService
{
/**
* DocuSign API Client
*/
public ApiClient $apiClient;
/**
* Create a new instance of our class.
*/
public function __construct()
{
$this->apiClient = OAuthClient::createApiClient();
}
/**
* Getter for the EnvelopesApi
*/
public function getEnvelopeApi(): EnvelopesApi
{
return new EnvelopesApi($this->apiClient);
}
}
Then, in my constructors where I want to use it I'm doing
/**
* Create a new controller instance
*/
public function __construct()
{
$this->clientService = new SignatureClientService();
$this->envelopesApi = $this->clientService->getEnvelopeApi();
}
Finally, I use it like so
$envelopeSummary = $this->envelopesApi->createEnvelope(config('docusign.api_account_id'), $envelopeDefinition);
But I get an error that reads
DocuSign\eSign\Client\ApiException: Error while requesting server,
received a non successful HTTP code [400] with response Body:
O:8:"stdClass":2:{s:9:"errorCode";s:21:"USER_LACKS_MEMBERSHIP";s:7:"message";s:60:"The
UserID does not have a valid membership in this Account.";} in
/homepages/45/d641872465/htdocs/sites/ita-portal/vendor/docusign/esign-client/src/Client/ApiClient.php:344
I researched this and this would imply that the user is not within the account, but they are. I also checked that this account owns the envelopes that I'm trying to send.
For reference I took inspiration for envelope sending from here: https://developers.docusign.com/docs/esign-rest-api/how-to/request-signature-template-remote/
What I think is happening is that the request is going to the wrong server or the wrong account.
I'd suggest using a packet analyser like Fiddler or Wireshark to log where your requests are headed (or just log the request within your application)
The auth URLs seem to be correct since you're not getting a 401 unauthorised error but the envelopes and other queries' must match the base URL located in your account under the Apps and Keys page. It would be of the form demo.docusign.net for our demo environment or xxx.docusign.net for our production environment
So I'm working with Google API Client for PHP and I have an OAuth flow that works,
class GoogleClient {
private static $client_id = "1050479587066-f64vq210hc2m15fdj4r77g8ml7jin30d.apps.googleusercontent.com";
private static $client_Secret = "CK8orQfPNpD9UgF0bqNJinVI";
private static $redirect_uri = '/return.php';
private static $access;
private static $client = null;
private static function checkForAccess(){
if(isset(self::$access)){
return true;
}
if(isset($_SESSION['GoogleAuth'])){
self::$access = $_SESSION['GoogleAuth'];
return true;
}
return false;
}
public static function GetClient(){
if(is_null(self::$client)){
$params = [
"client_id" => self::$client_id,
"client_secret" => self::$client_Secret,
"redirect_uri" => self::$redirect_uri,
"application_name" => "Test AdWords System"
];
if(self::checkForAccess() && self::isLoggedIn()){
$param["access_token"] = self::$access['access_token'];
}
//Create and Request to access Google API
$client = new Google_Client($params);
}
return $client;
}
public static function doLogin(){
$scopes = [ 'https://www.googleapis.com/auth/adwords', 'https://www.googleapis.com/auth/dfp', "https://www.googleapis.com/auth/userinfo.email"];
return self::GetClient()->createAuthUrl($scopes);
}
public static function doLoginFinal(){
if (!$code = $_GET['code']) {
throw new Exception("Auth Code is missing.");
}
$authResponse = self::GetClient()->authenticate($code);
if (isset($authResponse['error'])) {
throw new Exception(
"Unable to get access token.",
null,
new Exception(
"{$authResponse['error']} {$authResponse['error_description']}"
)
);
}
$_SESSION['GoogleAuth'] = $authResponse;
self::$access = $authResponse;
}
public static function isLoggedIn(){
if(self::checkForAccess()){
if(isset(self::$access)){
$expiresAt = #self::$access['created']+#self::$access['expires_in'];
return (time() < $expiresAt);
}
}
return false;
}
public static function GetExpiry(){
if(self::checkForAccess()){
return self::$access['created']+self::$access['expires_in'];
}
throw new Exception("The User is not logged into a google account.");
}
}
now this class is working I'm able to log in and I have the scope for google-adwords the problem comes about due to poor documentation for the googleads-php-lib
So from the example to getCampaigns it uses $oAuth2Credential = (new OAuth2TokenBuilder())->fromFile()->build(); but i don't have a file so i went into the OAuth2TokenBuilder file I'm unable to work out how i could give the already generated access tokens to the googleads objects.
I have double checked the google-php-api-client services repo and there is no adwords Service I can use.
I have been digging through the source files of the googleads-php-lib to see if I can find a method to implement this but so far I'm just getting stuck as everything seems to require specific parameter types so I can rig something to provide the details, but the code always seems to rely on multiple classes so I can't just build one that extends a class. and i pass that through.
Keys will be destoried after this test is working!
Well after days of digging around source files and hacking this and that I finally found an implementation that works.
After creating my manager account:
https://developers.google.com/adwords/api/docs/guides/signup
So this is the two new methods added to my GoogleClient Static Class
private static $developerToken = "";
private static function GetUserRefreshCredentials(){
return new UserRefreshCredentials(
null,
[
'client_id' => self::$client_id,
'client_secret' => self::$client_secret,
'refresh_token' => self::$access['refresh_token']
]
);
}
public function GetAdwordsSession(){
$builder = new AdWordsSessionBuilder();
$builder->defaultOptionals();
$builder->withDeveloperToken(slef::$developerToken);
return $builder->withOAuth2Credential(self::GetUserRefreshCredentials())->build();
}
I'm trying to access an uploaded file in the history middleware for Guzzle (v6).
My actual code receives a request (so is using the ServerRequestInterface), then uses Guzzle to send the request elsewhere.
I'm trying to test uploaded files going through this layer, but I can't seem to access them in the Request object returned by Guzzle's middleware.
Example code:
<?php
use GuzzleHttp\Client;
use GuzzleHttp\Handler\MockHandler;
use GuzzleHttp\HandlerStack;
use GuzzleHttp\Middleware;
use GuzzleHttp\Psr7\ServerRequest;
use GuzzleHttp\Psr7\UploadedFile;
class DoNotCommitTest extends \PHPUnit\Framework\TestCase
{
public function testUploads()
{
$request = new ServerRequest('GET', 'http://example.com/bla');
$file = new UploadedFile('test', 100, \UPLOAD_ERR_OK);
$request = $request->withUploadedFiles([$file]);
$this->assertCount(1, $request->getUploadedFiles());
// Mock Guzzle request, assert on the request it 'sent'
$mock = new MockHandler([
function (ServerRequest $request, array $options) {
// This fails...
$this->assertCount(1, $request->getUploadedFiles());
}
]);
$historyContainer = [];
$history = Middleware::history($historyContainer);
$handler = HandlerStack::create($mock);
$handler->push($history);
$client = new Client(['handler' => $handler]);
$client->send($request);
}
}
If you follow execution chain, $client->send($request) at some point calls private applyOptions function, which calls Psr7\modify_request function. If you look at Psr7\modify_request function:
...
if ($request instanceof ServerRequestInterface) {
return new ServerRequest(
isset($changes['method']) ? $changes['method'] : $request->getMethod(),
$uri,
$headers,
isset($changes['body']) ? $changes['body'] : $request->getBody(),
isset($changes['version'])
? $changes['version']
: $request->getProtocolVersion(),
$request->getServerParams()
);
}
...
It returns new ServerRequest object without preserving your uploaded files array (ServerRequest object doesn't have the uploadedFiles as an argument in the constructor). That's why you lost your uploadedFiles array.
UPDATE:
I created an issue and a pull request to fix it.
I have already written an application in a procedural way and am trying to move into into a Laravel framework. I'm having trouble with the SOAP exchange section as I am getting an ID value that authenticates the user but cannot access that value (as a cookie) later in the program to authenticate the search.
Here is my code so far:
<?php namespace App;
use Artisaninweb\SoapWrapper\Facades\SoapWrapper;
use Illuminate\Http\RedirectResponse;
class SoapController {
private $auth_response;
private $cookie;
private $search_client;
private $search_response;
public function soapExchange() {
// create SOAP client and add service details
SoapWrapper::add(function ($service) {
$service
->name('WoSAuthenticate')
->wsdl('http://search.webofknowledge.com/esti/wokmws/ws/WOKMWSAuthenticate?wsdl')
->trace(true)
->cache(WSDL_CACHE_NONE);
});
SoapWrapper::service('WoSAuthenticate', function($service) {
// call authenticate() method to get SID cookie
$auth_response = $service->call('authenticate', []);
$cookie = $auth_response->return;
// test for cookie return
// print($cookie);
});
// create SOAP client and add service details
$search_client = new SoapWrapper;
$search_client::add(function ($service) {
$service
->name('WoSSearch')
->wsdl('http://search.webofknowledge.com/esti/wokmws/ws/WokSearch?wsdl')
->trace(true)
->cache(WSDL_CACHE_NONE);
});
if (isset($auth_response->return)) {
// if there is an SID returned then add it to the cookie attribute of the search client
$search_client->__setCookie('SID', $cookie);
} else {
// route to relevant view to display throttle error
return redirect('throttle');
}
}
}
I am successfully retrieving the response from the Web API call and getting a code to authenticate the user, saved as $cookie. However, I need then to create another SoapWrapper for performing the search and this needs the ID code attached by using the __setCookie method. If nothing is returned by the authenticate call then it redirects to an error message via throttle.blade.php elsewhere.
Surely there is a way to return a value created from a function so that it can be used elsewhere?
** EDIT **
Looked into employing SoapClient instead and including all operations within a single function. It all relates to a specific Web API anyway so I guess separation of concerns is not so much of an issue. FYI the new class I am trying is this:
<?php namespace App\Models;
use SoapClient;
use Illuminate\Http\RedirectResponse;
class SoapWrapper {
public function soapExchange() {
// set WSDL for authentication and create new SOAP client
$auth_url = "http://search.webofknowledge.com/esti/wokmws/ws/WOKMWSAuthenticate?wsdl";
// array options are temporary and used to track request & response data
$auth_client = #new SoapClient($auth_url);
// set WSDL for search and create new SOAP client
$search_url = "http://search.webofknowledge.com/esti/wokmws/ws/WokSearch?wsdl";
// array options are temporary and used to track request & response data
$search_client = #new SoapClient($search_url);
// run 'authenticate' method and store as variable
$auth_response = $auth_client->authenticate();
// call 'setCookie' method on '$search_client' storing SID (Session ID) as the response (value) given from the 'authenticate' method
// check if an SID has been set, if not it means Throttle server has stopped the query, therefore display error message
if (isset($auth_response->return)) {
$search_client->__setCookie('SID',$auth_response->return);
} else {
return Redirect::route('throttle');
}
}
}
Maybe try $GLOBALS?
<?php
$GLOBALS[data] = "something";
function abc(){
echo $GLOBALS[data];
}
?>
use Artisaninweb\SoapWrapper\Facades\SoapWrapper;
class SoapController extends Controller {
public $resultSoapStatus;
public $resultSoapAuthority;
public function heySoap{
SoapWrapper::add(function ($service) ...
$data = [
'MerchantID' => $MerchantID,
'Amount' => $Amount,
'Description' => $Description,
'Email' => $Email,
'Mobile' => $Mobile,
'CallbackURL' => $CallbackURL
];
SoapWrapper::service('test', function ($service) use ($data) {
$resultSoap = $service->call('PaymentRequest', [$data]);
$this->resultSoapStatus = $resultSoap->Status;
$this->resultSoapAuthority = $resultSoap->Authority;
});
if($this->resultSoapStatus == 100 && strlen($this->resultSoapAuthority) == 36)
{
//Do Something
}
else
{
return Redirect::back();
}
}
}
Enjoy bro
I'm trying to access to the Guzzle Response object from Goutte. Because that object has nice methods that i want to use. getEffectiveUrl for example.
As far as i can see there is no way doing it without hacking the code.
Or without accessing the response object, is there a way to get the last redirected url froum goutte?
A little late, but:
If you are only interested in getting the URL you were last redirected to, you could simply do
$client = new Goutte\Client();
$crawler = $client->request('GET', 'http://www.example.com');
$url = $client->getHistory()->current()->getUri();
EDIT:
But, extending Goutte to serve your needs is fairly easy. All you need is to override the createResponse() method and store the GuzzleResponse
namespace Your\Name\Space;
class Client extends \Goutte\Client
{
protected $guzzleResponse;
protected function createResponse(\Guzzle\Http\Message\Response $response)
{
$this->guzzleResponse = $response;
return parent::createResponse($response);
}
/**
* #return \Guzzle\Http\Message\Response
*/
public function getGuzzleResponse()
{
return $this->guzzleResponse;
}
}
Then you can access the response object as desired
$client = new Your\Name\Space\Client();
$crawler = $client->request('GET', 'http://localhost/redirect');
$response = $client->getGuzzleResponse();
echo $response->getEffectiveUrl();