I have created an Angular 8 project and I used a wrapper for Google Places Autocomplete js library called ngx-google-places-autocomplete
The instructions are pretty straight forward and here is what I have so far in my project:
index.html
<script src="https://maps.googleapis.com/maps/api/js?key=<API_KEY>&libraries=places&language=en"></script>
app.module.ts
I have imported the library GooglePlaceModule as the following:
import { GooglePlaceModule } from "ngx-google-places-autocomplete";
#NgModule({
imports: [GooglePlaceModule, BrowserModule, FormsModule, ...],
....
})
app.component.html
<input
ngx-google-places-autocomplete
[options]='options'
placeholder="Enter a location"
autocomplete="off"
(onAddressChange)="handleAddressChange($event)"/>
app.component.ts
I have defined my options and the handleAddressChange() like the following
options: Options = {
bounds: undefined,
fields: ['address_component'],
strictBounds: false,
types: ['address'],
origin: undefined,
componentRestrictions: undefined
};
handleAddressChange(address: Address) {
console.log(address);
}
Yes, the autocomplete is working fine after I made sure I enabled Map Javascript API and Places API
However, the billing documentation wasn't clear where every key stroke was a an API request and that there is a way to get charged properly per user session (session token) so anything you do within your session does not count as an API request until handleAddressChange(address: Address) gets exectued.
My understanding, the documentation says that I need to inject a UUID (V4 recommended) into my script tag in my index.html like the following
<script src="https://maps.googleapis.com/maps/api/js?key=<API_KEY>&libraries=places&language=en&sessiontoken=<UUID>"></script>
I know I can use Google namespace to generate those UUIDs or sessionToken like the following:
var sessionToken = new google.maps.places.AutocompleteSessionToken();
However, I am not sure if there a proper way to inject the sessionToken into my <script> tag without violating XSS and I am not sure if this is the right way to implement a session tokens? I want to give the user one session token when the page loads so they can type/play with my search box they wanted with only one API request. I want to be in control of when the session token gets generated, injected, then destroyed in my app.
Those session tokens is used to REDUCE the amount of API requests so I don't get charged extra on my billing account.
Related
Google offers different ways to use the Place Autocomplete API. Two of these being the Autocomplete and AutocompleteService classes. Whenever I use the Autocomplete class I get the address components without a problem. However, whenever I use the AutocompleteService the address components are missing. I do get an address but not in the same way I do with Autocomplete (I can parse into city, street, state, etc.).
Is there a way I can get the address components field using AutocompleteService WITHOUT making an additional call with the place ID ?
Autocomplete example
function initAutocomplete() {
autocomplete = new google.maps.places.Autocomplete(document.getElementById('search'), {
componentRestrictions: { country: ["us", "ca"] },
fields: ["address_components", "geometry"],
types: ["address"],
});
autocomplete.addListener("place_changed", () => {
const place = autocomplete.getPlace(); // Includes Address Components and Geometry (Lat and Lng)
});
}
AutocompleteService example
function initAutocompleteService() {
const service = new google.maps.places.AutocompleteService();
service.getPlacePredictions({
input: "1600 Amphitheatre",
componentRestrictions: {
country: 'us'
},
types: ['address']
}, (predictions, status) => {
console.log(predictions); // It shows the address a single string but missing address components
});
}
Is there a way I can get the address components field using AutocompleteService WITHOUT making an additional call with the place ID ?
Short answer: No, you can't.
If you are worried about billing, you should use Session tokens.
As per the documentation:
AutocompleteService.getPlacePredictions() uses session tokens to group together autocomplete requests for billing purposes.
The interesting part:
You can use the same session token to make a single Place Details request on the place that results from a call to AutocompleteService.getPlacePredictions(). In this case, the autocomplete request is combined with the Place Details request, and the call is charged as a regular Place Details request. There is no charge for the autocomplete request.
Google provides an example on how to create the Session token:
// Create a new session token.
var sessionToken = new google.maps.places.AutocompleteSessionToken();
// Pass the token to the autocomplete service.
var autocompleteService = new google.maps.places.AutocompleteService();
autocompleteService.getPlacePredictions({
input: 'pizza near Syd',
sessionToken: sessionToken
},
displaySuggestions);
When user selects a place, you can reuse that same session token to retrieve the place details with the fields you are interested in, in order to get billed only for the place details request (as stated above).
You might also want to read Cost optimization best practices.
Programmatic implementation
Use a session token with your Place Autocomplete requests. When requesting Place Details about the selected prediction, include the following parameters:
The place ID from the Place Autocomplete response
The session token used in the Place Autocomplete request
The fields parameter specifying the place data fields you need
As you can see, the PlaceDetailsRequest interface takes an optional sessionToken parameter.
I currently have a Next.js application which have dynamic routing. I have URLs of this form /o/:organizationIdentifier*/p/:projectIdentifier*/e/:environmentIdentifier*/settings/notification/categories
I want to create a documentation which won't need to specify the organizationIdentifier, projectIdentifier or environmentIdentifier. A user will just click on a link like, /settings/notification/categories and it will redirect them to the /o/:organizationIdentifier*/p/:projectIdentifier*/e/:environmentIdentifier*/settings/notification/categories and these variables will be pulled from a redux store. However, I tried something like:
const rewrites = [
{
source: "/settings/notification/categories",
destination: "/o/:organizationIdentifier*/p/:projectIdentifier*/e/:environmentIdentifier*/settings/notification/categories",
"permanent":true,
"locale":false
}
]
but I keep getting errors:
destination has segments not in source or has
(organizationIdentifier, projectIdentifier, environmentIdentifier) for
route
{"source":"/settings/notification/categories","destination":"/o/:organizationIdentifier*/p/:projectIdentifier*/e/:environmentIdentifier*/settings/notification/categories","permanent":true,"locale":false}`
and
Error: Invalid redirects found
Any help on how I can achieve this functionality?
My Requirement is
1. User will type subject.
2. On the basis of subject, I want to call third party rest API (Currently being blocked by CORS. even the jsonp request is also not working)
3. I want to set some field values on form according to the response
You cannot render an Freshservice app from the customer's view (User/Employee/Requester) as of now. Instead you can create Freshservice app on Agent portal where tickets are managed.
Create a serverless app.
On onTicketCreate product event you can write in your logic in server.js:
exports = {
events: [{
event: "onTicketCreate",
callback: "onTicketCreateCallback"
}],
onTicketCreateCallback: function(payload) {
console.log("Logging arguments from onTicketCreate event: " + JSON.stringify(payload));
// 1.Implement the logic with the help of payload's ticket subject that you are looking for.
// 2. To shoot bypass CORS use platform's request API - https://developers.freshservice.com/docs/request-api/
// 3. Update the Fields using - https://api.freshservice.com/v2/#view_a_change
}
}
See this reference for making changes to agent facing field values.
In simple terms this process results to fill in fields as per your use case. Only difference is, it happens on creating a ticket but not while filling up the requester facing form.
I'm very new to liferay, I've created a page custom field using 'control panel > configuration > custom fields > page'. My goal is to retrieve the value from the page custom field and display the value in my custom portlet. One of the methods I've tried is using ExpandoValue/get-data API from the liferay json web service and this API is generated from localhost:8080/api/jsonws. Below is the generated javascript API:
Liferay.Service(
'/expandovalue/get-data',
{
companyId: themeDisplay.getCompanyId(),
className: 'com.liferay.portal.model.Page',
tableName: 'CUSTOM_FIELDS',
columnNames: 'pageDetail',
classPK: themeDisplay.getUserId()
},
function(obj) {
console.log(obj);
}
);
However, this api throws me an error: java.lang.NullPointerException. I'm thinking that this error occurs due to the permission given to the custom field. So, I've ticked View and Update permissions for Guest. But the issue persists.
My question is what triggered this error and how to fix it and is there any other solution I can use to retrieve the value from the page custom field?
Thanks in advance.
Edit
I had misunderstanding in assigning the attributes for the api and here is the new api as suggested by Olaf.
Liferay.Service(
'/expandovalue/get-data',
{
companyId: 20115,
className: 'com.liferay.portal.kernel.model.Layout',
tableName: 'CUSTOM_FIELDS',
columnName: 'details',
classPK: themeDisplay.getLayoutId()
},
function(obj) {
console.log(obj);
}
);
It works fine. However it only took the default value but not the value assign for that particular pages.
If memory serves me right, the stock remote API can't be used by unauthenticated users, but requires at least a logged in user (on top of the regular permissions of course).
You can test for this by accessing the API from logged in accounts with the same permissions. If it works there, then this is what you're running into.
However, when I interpret the call in your question correctly, the current user id would be the primary key for the custom field that you're looking at (looking at your classPK value). For a custom field on the page, I'd have expected a page id (layoutId in Liferay-API-terms). And while I'm seeing this, I notice com.liferay.portal.model.Page in your snippet. I've not seen that class, and pages can rather be found in com.liferay.portal.kernel.model.Layout (Assuming Liferay 7.1)
This makes me wonder what you're trying to achieve here - are you rather looking for a user-specific field (that would then be a custom field on the user) or really a page-specific field? Anyways - as this is an answer, not a comment, it might give enough hints to try out and come closer to a solution.
I've been told by BC support that this isn't possible, but I would be surprised if there really wasn't a way.
I need to be able to automatically assign a customer to a specific customer group when they create an account. My thought:
I would add an extra field to the sign-up form
Provide a user with a code (a string or number)
User enters code when creating new account
User hits submit
On form submit I would grab the value of the extra field:
var codeInput = document.getElementById('code-input').value;
I would then compare that value to a pre-defined string, and if there is a match, I would assign that customer to groupX (with a group id of 8):
if ( codeInput === "codeIGaveToTheUser" ) {
currentUserGroupID = 8;
}
Is it possible to assign a customer to a specific group on sign-up like this (or any other way)?
Any help is much appreciated.
Although using BigCommerce webhooks would ensure the highest success rate of executing your customer group assignment app, it requires quite a bit of setup on BigCommerce (creating a draft app, getting an oAuth key, jumping jacks, etc), and may be a bit of overkill for your requirements.
Here's an easier way, in my {mostly} humble opinion, that takes advantage of much of what you included in your original question. Any solution though will nonetheless require an external server to handle the customer group assignment through the BigCommerce API.
Within the BigCommerce control panel, add in the extra field to the user sign up form like you mentioned.
So as you can see, this new input field has been added natively to the default registration page:
So now, when a user creates an account on your site, the value for the Signup Code (the custom field created) will be directly accessible through the API for that customer's account. Take a look at what that JSON data looks like:
Okay, so this is nice and all, but how do we automate it?
To do so, we will have to let our external application know that a customer just registered. Furthermore, our external application will need some sort of reference to this newly created customer, so that it knows which customer to update the customer group for. Normally a BigCommerce webhook would notify us of all this, but since we aren't using a BigCommerce webhook, here's the alternative method to triggering the external script.
We will trigger our external application via the BigCommerce Registration Confirmation page - createaccount_thanks.html. This page is loaded immediately after a customer creates an account, so it is the perfect place to insert our trigger script.
Additionally, now that the customer is logged in, we can access the customer's email address via a BigCommerce Global system variable -%%GLOBAL_CurrentCustomerEmail%%.
We should make an HTTP request from this page to our external application along with the customer's email address. Specifically, we can make an XMLHttpRequest via JavaScript, or to be modern, we'll use Ajax via jQuery. This script should be inserted before the closing </body> tag on createaccount_thanks.html.
Example of POST request (although a GET would suffice as well):
<script>
$(function() {
$('.TitleHeading').text('One moment, we are finalizing your account. Please wait.').next().hide(); // Let the customer know they should wait a second before leaving this page.
//** Configure and Execute the HTTP POST Request! **//
$.ajax({
url: 'the_url_to_your_script.com/script.php',
type: 'POST',
contentType: 'application/json',
data: JSON.stringify({email:"%%GLOBAL_CurrentCustomerEmail%%"}),
success: function() {
// If the customer group assignment goes well, display page and proceed normally. This callback is only called if your script returns a 200 status code.
$('.TitleHeading').text('%%LNG_CreateAccountThanks%%').next().show();
},
error: function() {
// If the customer group assignment failed, you might want to tell your customer to contact you. This callback is called if your script returns any status except 200.
$('.TitleHeading').text('There was a problem creating your account').after('Please contact us at +1-123-456-7890 so that we can look into the matter. Please feel free to continue shopping in the meantime.');
}
});
});
</script>
Now finally, you just need to create your serverside application responsible for handling the request above, and updating the customer's customer group. You can use any language that you desire, and BigCommerce even offers several SDK's you can use to save mega development time. Just remember that you need to host it somewhere online, and then insert its URL to the JS script above.
PHP Example (quick & dirty):
git clone https://github.com/bigcommerce/bigcommerce-api-php.git
curl -sS https://getcomposer.org/installer | php && php composer.phar install
<?php
/**
* StackOverflow/BigCommerce :: Set Customer Group Example
* http://stackoverflow.com/questions/37201106/
*
* Automatically assigning a customer group.
*/
//--------------MAIN------------------------//
// Load Dependencies:
require ('bigcommerce-api-php/vendor/autoload.php');
use Bigcommerce\Api\Client as bc;
// Define BigCommerce API Credentials:
define('BC_PATH', 'https://store-abc123.mybigcommerce.com');
define('BC_USER', 'user');
define('BC_PASS', 'token');
// Load & Parse the Email From the Request Body;
$email = json_decode(file_get_contents('php://input'))->email;
// Execute Script if API Connection Good & Email Set:
if ($email && setConnection()) {
$customer = bc::getCollection('/customers?email=' .$email)[0]; //Load customer by email
$cgid = determineCustomerGroup($customer->form_fields[0]->value); //Determine the relevant customer group ID, via your own set string comparisons.
bc::updateCustomer($customer->id, array('customer_group_id' => $cgid)) ? http_send_status(200) : http_send_status(500); //Update the customer group.
} else {
http_send_status(500);
exit;
}
//-------------------------------------------------//
/**
* Sets & tests the API connection.
* #return bool true if the connection successful.
*/
function setConnection() {
try {
bc::configure(array(
'store_url' => BC_PATH,
'username' => BC_USER,
'api_key' => BC_PASS
));
} catch (Exception $e) {
return false;
}
return bc::getResource('/time') ? true : false; //Test Connection
}
/**
* Hard define the customer group & signup code associations here.
* #param string The code user used at signup.
* #return int The associated customergroup ID.
*/
function determineCustomerGroup($signupCode) {
switch ($signupCode) {
case 'test123':
return 1;
case 'codeIGaveToTheUser':
return 8;
default:
return 0;
}
}
So then you would do your customer group string comparisons directly in the serverside program. I'd recommend you rewrite your own BC API script as the one above in quality is really something along the lines of functional pseudo-code, but more so present to show the general idea. HTH
You would need to set up a server to listen for webhooks unless you wanted to do a cron job. We have some basic information on the developer portal, but I included more resources below. From there, you'd need to choose your server language of choice to listen for the webhooks once they been created, respond correctly (200 response if received), execute code based on this information, and then take action against the BC API.
So if you were looking for a code, you'd need to listen for the store/customer/created webhook, and have your code look for a custom field that contained the code. If it was present, then take action. Else, do nothing.
https://developer.github.com/webhooks/configuring/
http://coconut.co/how-to-create-webhooks
How do I receive Github Webhooks in Python