I want to get the response of getChat method in symfony for telegram but how use it?
https://api.telegram.org/bottoken/getChat?chat_id=#channelName
this is the response in postman:
{
"ok": true,
"result": {
"id": -1001000737610,
"title": "کانال 💯",
"username": "gizmiztel",
"type": "channel",
"description": "ارسال سوژه👈 #alogizmiz\n\nتبلیغات👈 #gizmiz_ad\n\nاینستاگرام👈 instagram.com/_u/gizmiztel\n\nسایت👈 gizmiz.com",
"photo": {
"small_file_id": "AQADBAATOIVnGQAElEEaamARX9hoWgIAAQI",
"big_file_id": "AQADBAATOIVnGQAE21YqJKH-YwZqWgIAAQI"
}
}
}
for send message I use this kind but for get i don't know what to do:
$result = [
'method' => 'sendMessage',
'chat_id' => $chatId,
'text' => 'خطای سرور'
];
return new JsonResponse($result, Response::HTTP_OK);
You can use Guzzle or PHP Curl for get result from any HTTP client.
For using guzzle http client:
Reqire "guzzlehttp/guzzle": "version" in your package.json and run composer update command.
Then you can use it like this:
<?php
namespace Foo\BarBundle\Driver;
use GuzzleHttp\Client;
class GetResultFromTelegram
{
protected client;
public function getInfoOfChannel()
{
$baseUrl = 'https://api.telegram.org/bottoken/getChatchat_id=#channelName';
$this->client = new Client();
$res = $this->client->request('GET', $baseUrl);
// Convert response to array.
return json_decode($res->getBody(), true);
}
}
Related
I would like to be able to access the GraphQL query inside of my AWS AppSync resolvers, but have not been able to figure out how to go about achieving this.
I use Terraform to create the resolver, like so:
resource "aws_appsync_resolver" "graphql_event_resolver" {
api_id = aws_appsync_graphql_api.appsync.id
type = "Query"
field = "event"
data_source = aws_appsync_datasource.graphql_datasource.name
request_template = <<EOF
{
"version" : "2017-02-28",
"operation": "Invoke",
"payload": {
"resolve": "event",
"arguments": $util.toJson($context.arguments)
}
}
EOF
response_template = var.response_template
}
Then I have a JavaScript resolver like the one below:
exports.handler = (event, context, callback) => {
console.log('VTL details: ', event);
callback(null, event);
};
I suspect the solution is to pass the query in the request template payload in the Terraform code where I create the resolver, but I have been unable to find any information on how to do this, so any help would be much appreciated.
Figured out, thanks to JWK's answer to this question, how to do it:
resource "aws_appsync_resolver" "graphql_event_resolver" {
api_id = aws_appsync_graphql_api.appsync.id
type = "Query"
field = "event"
data_source = aws_appsync_datasource.graphql_datasource.name
request_template = <<EOF
{
"version" : "2017-02-28",
"operation": "Invoke",
"payload": {
"resolve": "event",
"arguments": $util.toJson($context.arguments),
"query": $utils.toJson($context.info.selectionSetList)
}
}
EOF
response_template = var.response_template
}
Currently I have three separate servers. Client on :5001, API on :5002 and IdentityServer on :5003. I can athenticate my Blazor pages using #attribute [Authorize] but when I call the API I get a 401 error. If I past the token_id into postman and make a request to the API server it authenticates. If I make a request from my Blazor client it fails. I have whitelisted CORS to rule out that being the issue. If I remove the Audience check on the api with:
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
It works
Client program.cs
builder.Services.AddHttpClient("api")
.AddHttpMessageHandler(sp =>
{
var handler = sp.GetService<AuthorizationMessageHandler>()
.ConfigureHandler(
authorizedUrls: new[] { "https://localhost:5002" },
scopes: new[] { "coredesk" });
return handler;
});
builder.Services.AddScoped(
sp => sp.GetService<IHttpClientFactory>().CreateClient("api"));
builder.Services.AddOidcAuthentication(options =>
{
builder.Configuration.Bind("oidc", options.ProviderOptions);
});
Client appsettings.json
{
"oidc": {
"Authority": "https://localhost:5003/",
"ClientId": "coredesk",
"DefaultScopes": [
"openid",
"profile",
"coredesk"
],
"PostLogoutRedirectUri": "/",
"ResponseType": "code"
}
}
API Startup.cs
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "https://localhost:5003";
options.Audience = "coredesk";
});
IdentityServer Config.cs
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
};
public static IEnumerable<ApiResource> Apis =>
new ApiResource[]
{
new ApiResource("coredesk", "CoreDesk API")
};
public static IEnumerable<ApiScope> ApiScopes =>
new ApiScope[]
{
new ApiScope("coredesk"),
};
public static IEnumerable<Client> Clients =>
new Client[]
{
new Client
{
ClientId = "coredesk",
AllowedGrantTypes = GrantTypes.Code,
RequirePkce = true,
RequireClientSecret = false,
AllowedCorsOrigins = { "https://localhost:5001", "https://localhost:5002" },
AllowedScopes = { "openid", "profile", "coredesk" },
RedirectUris = { "https://localhost:5001/authentication/login-callback" },
PostLogoutRedirectUris = { "https://localhost:5001/" },
Enabled = true
},
};
if you look at the source code for AddJwtBearer you find this part:
// If no authorization header found, nothing to process further
if (string.IsNullOrEmpty(authorization))
{
return AuthenticateResult.NoResult();
}
if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
{
token = authorization.Substring("Bearer ".Length).Trim();
}
// If no token found, no further work possible
if (string.IsNullOrEmpty(token))
{
return AuthenticateResult.NoResult();
}
this shows that by default (without customization) it requires the token to be provided in the Authorization header, like this:
GET /api/payments HTTP/1.1
Host: localhost:7001
Authorization: Bearer eyJhbGciOiJSUzI1NiIsIYwA...
to further debug, I would remove the authorize attribute and then put a breakpoint in one of the action method and examine what the ClaimsPrincipal user object contains.
i need help with the api platform pagination in json format.
here is my api_platform.yaml
api_platform:
allow_plain_identifiers: true
mapping:
paths: ['%kernel.project_dir%/src/Entity']
formats:
json: ['application/json']
when i used the format hydra (default) i got something like this
"hydra:view": {
"#id": "/api/galleries?page=1",
"#type": "hydra:PartialCollectionView",
"hydra:first": "/api/galleries?page=1",
"hydra:last": "/api/galleries?page=6",
"hydra:next": "/api/galleries?page=2"
}
can anyone help me? if it's possible to get something like that in json format or another way to aply pagination with api platform or symfony
thank you
You shoud create an event Subscriber :
<?php
namespace App\EventSubscriber;
use ApiPlatform\Core\EventListener\EventPriorities;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpKernel\Event\ViewEvent;
use Symfony\Component\HttpKernel\KernelEvents;
use ApiPlatform\Core\Bridge\Doctrine\Orm\Paginator;
final class PaginateJsonSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
KernelEvents::VIEW => ['normalizePagination', EventPriorities::PRE_RESPOND],
];
}
public function normalizePagination(
ViewEvent $event
): void {
$method = $event->getRequest()->getMethod();
if ($method !== Request::METHOD_GET) {
return;
}
if (($data = $event->getRequest()->attributes->get('data')) && $data instanceof Paginator) {
$json = json_decode($event->getControllerResult(), true);
$pagination = [
'first' => 1,
'current' => $data->getCurrentPage(),
'last' => $data->getLastPage(),
'previous' => $data->getCurrentPage() - 1 <= 0 ? 1 : $data->getCurrentPage() - 1,
'next' => $data->getCurrentPage() + 1 > $data->getLastPage() ? $data->getLastPage() : $data->getCurrentPage() + 1,
'totalItems' => $data->getTotalItems(),
'parPage' => count($data)
];
$res = [
"data" => $json,
"pagination" => $pagination
];
$event->setControllerResult(json_encode($res));
}
}
}
In API Platform we have the jsonapi format which provides us the support for pagination. You can set the default config for number of records per page in api platform as:
# api/config/packages/api_platform.yaml
api_platform:
defaults:
pagination_items_per_page: 30 # Default value
formats:
jsonapi: ['application/vnd.api+json']
With this configuration you would require to either have an item and collection normalizer to customise the structure of the response with pagination metadata OR you can use state providers for this purpose (check here)
You would still get all the necessary pagination metadata if you just leave your default format as jsonapi
This is the very good resource to read more about API Platform pagination
The situation:
I need to send the request to the api to update the account info.
The API docs say I need to do send a PUT request to the API.
I trying to do this in Laravel 5.6, although I don't think this matters.
What I have so far:
A working constructor for the Guzzle client;
A working function to retrieve account info.
What is not working:
Upon submitting the request I get a Guzzle exception
Client error: \`PUT https://sandbox.proapi.itemize.com/api/enterprise/v1/accounts/<my account id>\` resulted in a \`400 Bad Request\` response: IOException:
This is the code I have so far:
<?php
namespace App\Http\Controllers;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\GuzzleException;
class ApiController extends Controller {
private $apiKey;
private $uri;
private $client;
public function __construct() {
$this->apiKey = 'my api key';
$this->uri = 'https://sandbox.proapi.itemize.com/api/enterprise/v1/accounts/my account id';
$this->client = new Client([
'base_uri' => $this->uri,
'auth' => [null, $this->apiKey]]);
}
public function accountInfo() {
$response = $this->client->request('GET','');
echo $response->getBody()->getContents();
}
public function updateAccountInfo() {
$response = $this->client->request('PUT','',[
'headers' => [
'Content-Type' => 'application/json',
],
'body' => '{"markets":"UK"}'
]);
echo $response->getBody()->getContents();
}
}
?>
400 Bad Request means: request sent by the client due to invalid syntax.
According itemize api documentation "markets" should be passed as an array of strings. And you can also use json format.
Try this:
public function updateAccountInfo() {
$response = $this->client->request('PUT', '', [
'json' => ["markets" => ["UK"]],
]);
echo $response->getBody()->getContents();
}
I am trying to reproduce the behaviour of the facebook batch requests function on their graph api.
So I think that the easiest solution is to make several requests on a controller to my application like:
public function batchAction (Request $request)
{
$requests = $request->all();
$responses = [];
foreach ($requests as $req) {
$response = $this->get('some_http_client')
->request($req['method'],$req['relative_url'],$req['options']);
$responses[] = [
'method' => $req['method'],
'url' => $req['url'],
'code' => $response->getCode(),
'headers' => $response->getHeaders(),
'body' => $response->getContent()
]
}
return new JsonResponse($responses)
}
So with this solution, I think that my functional tests would be green.
However, I fill like initializing the service container X times might make the application much slower. Because for each request, every bundle is built, the service container is rebuilt each time etc...
Do you see any other solution for my problem?
In other words, do I need to make complete new HTTP requests to my server to get responses from other controllers in my application?
Thank you in advance for your advices!
Internally Symfony handle a Request with the http_kernel component. So you can simulate a Request for every batch action you want to execute and then pass it to the http_kernel component and then elaborate the result.
Consider this Example controller:
/**
* #Route("/batchAction", name="batchAction")
*/
public function batchAction()
{
// Simulate a batch request of existing route
$requests = [
[
'method' => 'GET',
'relative_url' => '/b',
'options' => 'a=b&cd',
],
[
'method' => 'GET',
'relative_url' => '/c',
'options' => 'a=b&cd',
],
];
$kernel = $this->get('http_kernel');
$responses = [];
foreach($requests as $aRequest){
// Construct a query params. Is only an example i don't know your input
$options=[];
parse_str($aRequest['options'], $options);
// Construct a new request object for each batch request
$req = Request::create(
$aRequest['relative_url'],
$aRequest['method'],
$options
);
// process the request
// TODO handle exception
$response = $kernel->handle($req);
$responses[] = [
'method' => $aRequest['method'],
'url' => $aRequest['relative_url'],
'code' => $response->getStatusCode(),
'headers' => $response->headers,
'body' => $response->getContent()
];
}
return new JsonResponse($responses);
}
With the following controller method:
/**
* #Route("/a", name="route_a_")
*/
public function aAction(Request $request)
{
return new Response('A');
}
/**
* #Route("/b", name="route_b_")
*/
public function bAction(Request $request)
{
return new Response('B');
}
/**
* #Route("/c", name="route_c_")
*/
public function cAction(Request $request)
{
return new Response('C');
}
The output of the request will be:
[
{"method":"GET","url":"\/b","code":200,"headers":{},"body":"B"},
{"method":"GET","url":"\/c","code":200,"headers":{},"body":"C"}
]
PS: I hope that I have correctly understand what you need.
There are ways to optimise test-speed, both with PHPunit configuration (for example, xdebug config, or running the tests with the phpdbg SAPI instead of including the Xdebug module into the usual PHP instance).
Because the code will always be running the AppKernel class, you can also put some optimisations in there for specific environments - including initiali[zs]ing the container less often during a test.
I'm using one such example by Kris Wallsmith. Here is his sample code.
class AppKernel extends Kernel
{
// ... registerBundles() etc
// In dev & test, you can also set the cache/log directories
// with getCacheDir() & getLogDir() to a ramdrive (/tmpfs).
// particularly useful when running in VirtualBox
protected function initializeContainer()
{
static $first = true;
if ('test' !== $this->getEnvironment()) {
parent::initializeContainer();
return;
}
$debug = $this->debug;
if (!$first) {
// disable debug mode on all but the first initialization
$this->debug = false;
}
// will not work with --process-isolation
$first = false;
try {
parent::initializeContainer();
} catch (\Exception $e) {
$this->debug = $debug;
throw $e;
}
$this->debug = $debug;
}