How to access URI query parameters from a Resource class using Apigility? - zend-framework2

I'm developing an Apigility driven application based on the Zend Framework 2.
I want my application to provide nested responses for both -- single items and lists:
/projects/1
{
"id": "1",
"title": "...",
...
"_embedded": {
"images": [
{
"id": "1",
"project_id": "1",
"title": "...",
...
},
{
"id": "2",
"project_id": "1",
"title": "...",
...
}
]
},
...
}
/projects
{
...
"_embedded": {
"projects": [
{
"id": "1",
"title": "...",
...
"_embedded": {
"images": [
{
"id": "1",
"project_id": "1",
"title": "...",
...
},
...
]
},
...
},
...
]
},
"total_items": 2
}
Since I've not found an apigility conform solution for implementing lists with nested lists (in this case projects with a list of images for every project list item, see here). I have to deal with the Paginator and DbAdapter and provide the page parameter manually:
class ProjectResource extends AbstractResourceListener {
...
public function fetchAll($params = array()) {
$pageNumber = $this->getEvent()->getRouteMatch()->getParam('page', 1); <-- that doesn't work
$projectService = $this->getProjectService();
$offset = $pageNumber > 0 ? $pageNumber - 1 : 0;
$config = $this->getServiceManager()->get('Config');
$itemCountPerPage = $config['zf-rest']['Portfolio\\V2\\Rest\\Project\\Controller']['page_size'];
$projects = $projectService->getProjects($offset, $itemCountPerPage);
return $projects;
}
...
}
The problem is, that $this->getEvent()->getRouteMatch()->getParam('page', 1) doesn't work. Instead of the page parameter, $this->getEvent()->getRouteMatch()->getParams() returns
Array
(
[controller] => Portfolio\V2\Rest\Project\Controller
[version] => 2
)
How to access request parameters?

Request parameters have first to be added onto the whitelist. It can be done over the Apigility GUI or directly in the config:
module.config.php
return array(
...
'zf-rest' => array(
...
'Portfolio\\V2\\Rest\\Project\\Controller' => array(
...
'collection_query_whitelist' => array('page'),
...
),
...
),
);
Then the parameter can be accessed over the arguments of the end point methods of the Resource class:
public function fetchAll($params = array()) {
$projectService = $this->getProjectService();
$config = $this->getServiceManager()->get('Config');
$itemCountPerPage = $config['zf-rest']['Portfolio\\V2\\Rest\\Project\\Controller']['page_size'];
$pageNumber = isset($params['page']) && intval($params['page']) > 0
? $params['page']
: 1
;
$offset = ($pageNumber - 1) * $itemCountPerPage;
$projects = $projectService->getProjects($offset, $itemCountPerPage);
return $projects;
}
See also the Apiglity documentation: ZF REST -> Configuration -> User Configuration -> collection_query_whitelist.

Related

Elasticsearch Find Out does user stops or moving - Possible?

I want to use elasticsearch configuration about mapping to display user location and his/her direction to admin in my web app. so I create an index in elasticsearch like:
{
"settings": {
"index": {
"number_of_shards": 5,
"number_of_replicas": 1
},
"analysis": {
"analyzer": {
"analyzer-name": {
"type": "custom",
"tokenizer": "keyword",
"filter": "lowercase"
}
}
}
},
"mappings": {
"properties": {
"driver_id": { "type": "integer" },
"email": { "type": "text" },
"location": { "type": "geo_point" },
"app-platform": { "type": "text" },
"app-version": { "type": "text" },
"created_at": { "type": "date", "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"}
}
}
}
and start to inserting user location to elasticsearch with this curl
{
"driver_id": 357,
"driver_email": "Andrew#mailinatior.com",
"location": {
"lat": 37.3,
"lon": 59.52
},
"created_at": "2021-06-04 00:09:00"
}
this structure came from user mobile to my elasticsearch, after that I wrote these services to fetch data for my web-end part of my designing:
module Api
module V1
module Drivers
module Elastic
class LiveLocation
include Peafowl
attribute :driver_id, ::Integer
def call
#driver = ::Driver.find(driver_id) if driver_id.present?
result = []
options = {
headers: {
'Content-Type' => 'application/json'
},
body: #driver.present? ? options_with_driver : options
}
begin
response = HTTParty.get(elasticseach_url.to_s, options)
records = JSON.parse(response.body)['hits']['hits']
if records.present?
records.group_by { |r| r['_source']['driver_id'] }.to_a.each do |record|
driver = ::Driver.where(id: record[0]).first
if driver.present?
location = record[1][0]['_source']['location']
app_platform = record[1][0]['_source']['app-platform']
app_version = record[1][0]['_source']['app-version']
result.push(driver_id: driver.id, driver_email: driver.profile.email, location: location, app_platform: app_platform, app_version: app_version)
end
end
end
rescue StandardError => error
Rails.logger.info "Error => #{error}"
result = []
end
context[:response] = result
end
def elasticseach_url
"#{ENV.fetch('ELASTICSEARCH_BASE_URL', 'http://127.0.0.1:9200')}/#{ENV.fetch('ELASTICSEARCH_DRIVER_POSITION_INDEX', 'live_location')}/_search"
end
def options
{
query: {
bool: {
filter: [
{
range: {
created_at: {
gte: (Time.now.beginning_of_day.strftime '%Y-%m-%d %H:%M:%S')
}
}
}
]
}
},
sort: [
{
created_at: {
order: 'desc'
}
}
]
}.to_json
end
def optinos_with_driver
{
query: {
bool: {
must: [
{
term: {
driver_id: {
value: #driver.id
}
}
}
],
filter: [
{
range: {
created_at: {
gte: (Time.now.beginning_of_day.strftime '%Y-%m-%d %H:%M:%S')
}
}
}
]
}
},
sort: [
{
created_at: {
order: 'desc'
}
}
]
}.to_json
end
end
end
end
end
end
this structure working perfectly but even if the user stops while elasticsearch saves his location but I need to filter user data that if the user stops for one hour in place elasticsearch understand and not saving data. Is it possible?
I use elsticsearch 7.1
and ruby 2.5
I know it's possible in kibana but I could not using kibana at this tim.
I am not sure if this can be done via a single ES query...
However you can use 2 queries:
one to check if the user's location's during the last hour is the same
Second same then don't insert
But i don't recommend that
What you could do:
Use REDIS or any in-mem cache to maintain the user's last geo-location duration
Basis that, update or skip update to Elastic Search
PS: I am not familiar with ES geo-location API

proper syntax for generating JSON body in API call

With the following long controller action code
#available = Available.find(694)
#tareservation_id = 8943
#request_date_time = Time.now.utc.iso8601
#request_id = Time.now.to_i
#in_date = (Date.today + 24.days).strftime("%Y-%m-%d").to_s
#book = %Q|{
"booking": {
"currencyCode": "USD",
"languageCode": "es",
"paxNationality": "ES",
"clientRef": {
"value": \"#{#tareservation_id}\",
"mustBeUnique": true
},
"items": [
{
"itemNumber": 1,
"immediateConfirmationRequired": true,
"productCode": \"#{#available.product_code}\",
"leadPaxName":
{ "firstName": "Guy",
"lastName": "Test"
},
"product":
{
"period":
{
"start": "2018-08-27",
"quantity": 2
}
}
} ]
},
"requestAuditInfo":
{ "agentCode": "001",
"requestPassword": "pass",
"requestDateTime": \"#{#requestDateTime}\",
"requestID": #{#request_id} },
"versionNumber": "2.0"
}|
This then must be shipped off to the API as JSON in the body call
#result = HTTParty.post(
'https://test.com/search',
:body => JSON.parse(#book).to_json,
headers: {
'Content-Type' => 'application/json',
'Accept' => 'application/json',
'Connection' => 'Keep-Alive'
}
)
If the following block is removed:
,
"product":
{
"period":
{
"start": "2018-08-27",
"quantity": 2
}
}
in console JSON.parse(#start), parses properly. With the block JSON::ParserError: 784: unexpected token. Yet I fail to see what is incorrect here?
Is Rails handling of string for future JSON conversion really strict on syntax, particularly since there is interpretation of instance variables - both as strings and integers - and har returns involved? What would it be then? Or is there a safer solution to get out of what quickly becomes quicksand?
It turns out that pasting too many lines into the console (iTerm2, in this case) does something to the memory. 25 lines of code pasted in a single time is the maximum observered where behaviour is as expected.

Post method with multiple parameter

I am unable to insert multiple rows in database using Post method in MVC web API. I have written code for it but when i am testing by inserting multiple rows through postman it is giving error. At line first the variable "delegatetable" shows null due to which error is coming. i am not doing database connection through entity framework, i have created a DelegateTable class.
public HttpResponseMessage Post(List<DelegateTable> delegatetable)
{
try
{
using (var delegateContext = new ShowContext())
{
foreach (DelegateTable item in delegatetable)
{
DelegateTable delegates = new DelegateTable();
delegates.Salutation__c = item.Salutation__c;
delegates.First_Name__c = item.First_Name__c;
delegates.Last_Name__c = item.Last_Name__c;
delegates.Account_Name__c = item.Account_Name__c;
delegates.Contact_Email__c = item.Contact_Email__c;
delegates.Category__c = item.Category__c;
delegates.Conference_Type__c = item.Conference_Type__c;
delegates.Conference_Selection__c = item.Conference_Selection__c;
delegates.Payment_Statuss__c = item.Payment_Statuss__c;
delegates.Barcode__c = item.Barcode__c;
delegateContext.SaveChanges();
}
var message = Request.CreateResponse(HttpStatusCode.Created, delegatetable);
message.Headers.Location = new Uri(Request.RequestUri.ToString());
return message;
}
}
catch (Exception ex)
{
return Request.CreateErrorResponse(HttpStatusCode.BadRequest, ex);
}
}
Json data that i am passing is below
[
{
"attributes": {
"type": "Registration__c",
"url": "/services/data/v43.0/sobjects/Registration__c/a3h8E0000009VuVQAU"
},
"Salutation__c": "Dr.",
"First_Name__c": "Test",
"Last_Name__c": "Test",
"Account_Name__c": "Test",
"Contact_Email__c": "test123#gmail.com",
"Category__c": "Test",
"Conference_Type__c": null,
"Conference_Selection__c": null,
"Payment_Statuss__c": null,
"Barcode__c": "Test"
},
{
"attributes": {
"type": "Registration__c",
"url": "/services/data/v43.0/sobjects/Registration__c/a3hD0000001kEfOIAU"
},
"Salutation__c": "Mr.",
"First_Name__c": "Demo",
"Last_Name__c": "Demo",
"Account_Name__c": "Demo",
"Contact_Email__c": "Demo#gmail.com",
"Category__c": "Demo",
"Conference_Type__c": null,
"Conference_Selection__c": null,
"Payment_Statuss__c": null,
"Barcode__c": null
}
]
You may try to reformat your payload as a JSON array, as the problem might be that the payload cannot be converted to a List.
Try this:
{
"delegates" :
[
{
"attributes": ..., ...
},
{ "attributes": ..., ...
},
...
]
}

Add bpmn:Resource in propertiesPanel

Is it possible to add "bpmn:Resource" element in propertiesPanel and edit its attribute? how to do it?
I've already added some properties to property panel based on the property-panel[link] example.
But I have a challenge in adding 'bpmn:Resource' to the properties panel. I don't want it to be added as an extensionElement.
I'd like it to be inside bpmn:Definitions (beside bpmn:Process). Also I'd like to extend the original bpmn:Resource to add some parameters.
So in MyModdle.json I added:
{
"name": "Resource",
"extends": [
"bpmn:Resource"
],
"properties": [
{
"name": "parameters",
"isMany": true,
"type": "MyParameter"
}
]
}, {
"name": "MyParameter",
"properties": [
{
"name": "myParameterType",
"isAttr": true,
"type": "String"
}
{
"name": "myParameterName",
"isAttr": true,
"type": "String"
},
{
"name": "myParameterValue",
"isAttr": true,
"type": "String"
}
]
}
now for example in newElement function of MyResource.js
var newElement = function (type, prop, factory) {
return function (element, extensionElements, value) {
var commands = [];
var resource = getResource(element);
if (!resource) {
var parent = extensionElements;
resource = createResource(parent, bpmnFactory);
console.log('resource', resource);
commands.push(cmdHelper.addAndRemoveElementsFromList(
element,
extensionElements,
'values',
'extensionElements',
[resource],
[]
));
}
var newElem = createResourceParameter(type, resource, bpmnFactory, {
resourceId: 'id-' + value
});
commands.push(cmdHelper.addElementsTolist(element, parameters, prop, [newElem]));
return commands;
};
}
I know this cmdHelper adds 'bpmn:Resource' to extensionElements but I don't know what to use instead!

Fatal error: Class 'SendGrid' not found

I've written a function to send emails using SendGrid's PHP library. It works everywhere except when I try to use it on a form submit event.
When clicking submit I get an error
Fatal error: Class 'SendGrid' not found in ... on line 72
Everything else on submit works fine, the post is added to the database, but the email isn't sent out.
Here is the function
function send_email( $post_id, $templatecode ) {
$substitutions = '
"-FNAME-" : "'.$primary_firstname.'",
"-NAME-" : "'.$primary_name.'",
"-EMAIL-" : "'.$primary_email.'"
';
$to = '
"email": "'.$primary_email.'",
"name": "'.$primary_name.'"
';
$apiKey = getenv('SENDGRID_API_KEY');
$sg = new \SendGrid($apiKey);
$request_body = json_decode('{
"personalizations": [ {
"substitutions": {'.$substitutions.'},
"to": [ { '.$to.'} ]
} ],
"template_id": "'.$template.'",
"categories": [
"'.$templatecode.'"
],
"from": {
"email": "xxxx",
"name": "xxxxx"
}
}');
$response = $sg->client->mail()->send()->post($request_body);
}
}
Here is the submit action
if( 'POST' == $_SERVER['REQUEST_METHOD'] && !empty( $_POST['action'] ) && $_POST['action'] == "new_post") {
$new_post = array(
'post_title' => wp_strip_all_tags( $_POST['title'] ),
'post_content' => $_POST['description'],
'post_status' => 'publish',
'post_type' => 'sessions'
);
$post_id = wp_insert_post($new_post);
send_email( $post_id, 'Submit' );
}

Resources