AdWords is now Google Ads. Our new name reflects the full range of advertising options we offer across Search, Display, YouTube, and more. Learn more

My Business
1.5K members online now
1.5K members online now
For developers who are using the Google My Business API to manage locations
Guide Me
star_border
Reply

Using service account

[ Edited ]
Visitor ✭ ✭ ✭
# 1
Visitor ✭ ✭ ✭

Hi,

 

Having got the sample code to work, my next task was to get it working with a service account and create a new location using the MyBusinessClient.createLocation as a template; using the example authorization code that TerryW gave me, I can authorize, but I cannot create a new location because I get this exception:

 

Exception in thread "main" com.google.api.client.googleapis.json.GoogleJsonResponseException: 404 Not Found

{

  "code" : 404,

  "message" : "Requested entity was not found.",

  "status" : "NOT_FOUND"

}

 

The exception occurs when calling Location createdLocation = createLocation.execute().  I have added 

createLocation.getRequestHeaders().set("X-GOOG-API-FORMAT-VERSION", 2); to increase the debug output, but nothing more is output.

 

What's going on?

 

I have also used the example authorize() from the docs instead of Terry's and the code works just fine except that the location is created underneath a completely different account that I cannot list or access in the playground

 

Thanks

John

1 Expert replyverified_user

Re: Using service account

Google Employee
# 2
Google Employee

Hi @SPAR D,

 

According to our community guidelines, you should search for the answer before you ask. When you search in the Google My Business API community for the answer, please use the search box at the top of the page and filter your search results “By location” and select the “Google My Business API” board. This will limit your search results to the topics created in the Google My Business API board only. You can click on “Advanced Search…” to the right of the search box on the search results page to view results by “Specific posts” for the answer found in posts under a topic.

 

It sounds like you are having exactly the same issue as reported in this thread, which you should check out for the provided solution.

 

The following are the required steps you should take for the service account application to work with the Google My Business API:

/**

 * Authorizes the installed application to access user's protected data using a service account.

 */

private static Credential authorize() throws Exception {

  GoogleCredential credential =

      new GoogleCredential.Builder()

          .setTransport(httpTransport)

          .setJsonFactory(JSON_FACTORY)

          .setServiceAccountId(

              "****@developer.gserviceaccount.com")

          .setServiceAccountScopes(

              Collections.singleton("https://www.googleapis.com/auth/plus.business.manage"))

          .setServiceAccountPrivateKeyFromP12File(new File("key.p12"))

          // Set the user you are impersonating. This should be a valid login email

          // for your Google My Business account you are making calls to.

          .setServiceAccountUser("user@example.com")

          .build();

  credential.refreshToken();

  return credential;

}

 public static Location createLocation(Account account) throws Exception {

   // Street address

   List<String> addressLines = new ArrayList<>();

   addressLines.add("Level 5, 48 Pirrama Road");

   Address address =

       new Address()

           .setAddressLines(addressLines)

           .setLocality("Pyrmont")

           .setAdministrativeArea("NSW")

           .setCountry("AU")

           .setPostalCode("2009");

 

   // Business hours

   List<TimePeriod> periods = new ArrayList<>();

   List<String> days = Arrays.asList("Monday", "Tuesday", "Wednesday", "Thursday", "Friday");

 

   for (String day : days) {

     TimePeriod period =

         new TimePeriod()

             .setOpenDay(day)

             .setOpenTime("8:00")

             .setCloseTime("13:00")

             .setCloseDay(day);

     periods.add(period);

     period =

         new TimePeriod()

             .setOpenDay(day)

             .setOpenTime("14:00")

             .setCloseTime("19:00")

             .setCloseDay(day);

     periods.add(period);

   }

   TimePeriod period =

       new TimePeriod()

           .setOpenDay("Saturday")

           .setOpenTime("9:00")

           .setCloseTime("14:00")

           .setCloseDay("Saturday");

   periods.add(period);

   BusinessHours businessHours = new BusinessHours().setPeriods(periods);

 

   // Additional Categories

   List<Category> additionalCategories = new ArrayList<>();

   additionalCategories.add(

       new Category().setName("Corporate Office").setCategoryId("gcid:corporate_office"));

 

   // Labels

   List<String> labels = new ArrayList<>();

   labels.add("Recently opened");

   labels.add("High-traffic");

 

   // Example of setting special hours (which supersede regular hours)

   Date newYearsEve = new Date().setYear(2015).setMonth(12).setDay(31);

   Date newYearsDay = new Date().setYear(2016).setMonth(1).setDay(1);

   List<SpecialHourPeriod> exceptionalPeriods = new ArrayList<>();

   exceptionalPeriods.add(

       new SpecialHourPeriod()

           .setStartDate(newYearsEve)

           .setOpenTime("11:00")

           .setCloseTime("23:30")

           .setEndDate(newYearsEve));

   exceptionalPeriods.add(new SpecialHourPeriod().setStartDate(newYearsDay).setIsClosed(true));

   SpecialHours holidayHours = new SpecialHours().setSpecialHourPeriods(exceptionalPeriods);

 

   // OpenInfo

   OpenInfo openInfo = new OpenInfo();

   openInfo.setStatus("OPEN");

 

   Location location =

       new Location()

           .setAddress(address)

           .setRegularHours(businessHours)

           .setLocationName("Google Sydney")

           .setStoreCode("GOOG-SYD")

           .setPrimaryPhone("(02) 9374 4000")

           .setPrimaryCategory(

               new Category().setName("Software Company").setCategoryId("gcid:software_company"))

           .setAdditionalCategories(additionalCategories)

           .setWebsiteUrl("https://www.google.com.au/")

           .setLabels(labels)

           .setSpecialHours(holidayHours)

           .setOpenInfo(openInfo);

 

   Mybusiness.Accounts.Locations.Create createLocation =

       mybusiness.accounts().locations().create(account.getName(), location);

   createLocation

       .setRequestId(UUID.randomUUID().toString())

       .setLanguageCode("en")

       .setValidateOnly(false)

       // If you get a request validation error you can inject the following header into any call as

       // shown below to get a more descriptive error and then it will throw trying to digest that

       // richer message.

       // This is a bug in the client library generator that will be fixed in the future.

       .getRequestHeaders()

       .set("X-GOOG-API-FORMAT-VERSION", 2);

   Location createdLocation = createLocation.execute();

 

   return createdLocation;

 }

 

Please perform the above steps to get your service account application to work, and report back to me if you have any other issues.

 

Thanks,

Terry

Using service account

Visitor ✭ ✭ ✭
# 3
Visitor ✭ ✭ ✭

Hi Terry,

 

I am surprised to see your response is instructions on how to search, it comes across as vaguely patronising but I'm sure you don't mean it like that - you may be surprised to note that I had searched but not found anything which seemed to fit the bill and that is why I posted. 

 

That post you linked to seems to be discussing a different problem, that locations do not appear in the Business UI, as opposed than an error when trying to create a location.  Interestingly, that was my earlier problem which I did not post about - at the time I was following the official documentation snippet for authorization (from a .json file).  I switched to using the authorisation example that you had previously posted in reply to my earlier question, and then started getting the exception above; this lead me to believe that my createLocation code is correct as only the authorize() changed.

 

Because I had read the forum and your previous posts to me (according to your community guidelines) I had already found the Apps connected to your account page and seen what I believed to be my application listed, which is why I discounted that as an issue.  

 

Thanks for the long copy and paste - although as I mentioned in my original post, I am already using the snippet you have previously provided for authorising, and I am using the createLocation from the client sample application that I have working when using OAuth Client ID.

 

I do take your point about the steps for setting up an account - I'll go through and redo the steps on Monday and let you know if there is still an issue.

 

Regards

John

Re: Using service account

Visitor ✭ ✭ ✭
# 4
Visitor ✭ ✭ ✭

Hi @Terry W,

 

 

dataStoreFactory = new FileDataStoreFactory(DATA_STORE_DIR);
credential = authorize();

private static Credential authorize() throws Exception {
		// Creates an InputStream to hold the client ID and secret.
		InputStream secrets = null;

		try {
			secrets = Main.class.getResourceAsStream("/client_secrets.json");
		} catch (Exception e) {
			logger.error("GoogleMyBusiness - error while reading the secrets stream " + e);
		}

		// Prompts the user if no credential is found.
		// TODO : Appropriate error handling
		if (secrets == null) {
			if (logger.isDebugEnabled()){
				logger.debug("Enter Client ID and Secret from Google API Console into WEB-INF/classes/client_secrets.json");
			}
			throw new Exception("Enter Client ID and Secret from Google API Console into WEB-INF/classes/client_secrets.json");
		}

		// Uses the InputStream to create an instance of GoogleClientSecrets.
		GoogleClientSecrets clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, new InputStreamReader(secrets));

		// TODO : Appropriate error handling
		if (clientSecrets.getDetails().getClientId().startsWith("Enter") || clientSecrets.getDetails().getClientSecret().startsWith("Enter ")) {
			if (logger.isDebugEnabled()){
				logger.debug("Enter Client ID and Secret from Google API Console into WEB-INF/classes/client_secrets.json");
			}
			throw new Exception("Enter Client ID and Secret from Google API Console into WEB-INF/classes/client_secrets.json");
		}

		// Sets up the authorization code flow.
		GoogleAuthorizationCodeFlow flow = new GoogleAuthorizationCodeFlow.Builder(httpTransport, JSON_FACTORY, clientSecrets, 
				Collections.singleton("https://www.googleapis.com/auth/plus.business.manage")).setDataStoreFactory(dataStoreFactory).build();

		// Returns the credential.
		return new AuthorizationCodeInstalledApp(flow, new LocalServerReceiver()).authorize("user");
	}

I have the exact same problem. The above code works well in my DEV environment. I get a google screen to enter the email which has the access to locations & then it redirects me back to http://localhost:SOME_PORT/ and I can view/update the locations.

 

 

Now we moved to QA. Firstly, the QA server is not accessible via internet and doesn't have direct internet connectivity and we have to go via the proxy. 

So I added the below code 

 

			Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_IP, Integer.parseInt(PROXY_PORT)));
			httpTransport = new NetHttpTransport.Builder().setProxy(proxy).build();

Now I get a message in the standard system out console of my JAVA application 

 

Please open the following URL in your browser:
  https://accounts.google.com/o/oauth2/auth?client_id=*****&redirect_uri=http://localhost:50835/Callback&response_type=code&scope=https://www.googleapis.com/auth/plus.business.manage

Now I'm not sure why there is a change in behaviour here. I even tried adding my QA host to OAuth client ID's Authorised redirect URIs , but no luck.

 

Nevertheless I tried to switch to Service Accounts now. And changed my code as 

			credential = new GoogleCredential.Builder()
				    .setTransport(httpTransport)
				    .setJsonFactory(JSON_FACTORY)
				    .setServiceAccountId(emailAddress)
				    .setServiceAccountPrivateKeyFromP12File(new File("$FILE.p12"))
				    .setServiceAccountScopes(Collections.singleton("https://www.googleapis.com/auth/plus.business.manage"))
				    //.setServiceAccountUser("$USEREMAILADDRESS")
				    .build();
			credential.refreshToken();
			
		    if (!credential.refreshToken()) {
		    	System.out.println("NO REFRESH TOKEN **** \n\n ");
		    	throw new RuntimeException("Failed OAuth to refresh the token");
		    } else {
		    	System.out.println("YES REFRESH TOKEN **** \n\n ");
		    }

 

 

I tried both ways, using the setServiceAccountUser method and commenting out.

If I use setServiceAccountUser method above, I don't get the access token and 401 Unauthorized is displayed 

com.google.api.client.auth.oauth2.TokenResponseException: 401 Unauthorized
	at com.google.api.client.auth.oauth2.TokenResponseException.from(TokenResponseException.java:105)
	at com.google.api.client.auth.oauth2.TokenRequest.executeUnparsed(TokenRequest.java:287)
	at com.google.api.client.auth.oauth2.TokenRequest.execute(TokenRequest.java:307)
	at com.google.api.client.googleapis.auth.oauth2.GoogleCredential.executeRefreshToken(GoogleCredential.java:384)
	at com.google.api.client.auth.oauth2.Credential.refreshToken(Credential.java:489)
	at com.google.api.client.auth.oauth2.Credential.intercept(Credential.java:217)
	at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:868)
	at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:419)
	at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnparsed(AbstractGoogleClientRequest.java:352)
	at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.execute(AbstractGoogleClientRequest.java:469)
	at uk.co.ee.maps.mybusiness.GoogleMyBusiness.listLocations(GoogleMyBusiness.java:192)

 If I comment out setServiceAccountUser method above, I DO get the access token, but 404 Not Found is displayed 

om.google.api.client.googleapis.json.GoogleJsonResponseException:  404 Not Found
{
  "code" : 404,
  "message" : "Requested entity was not found.",
  "status" : "NOT_FOUND"
}
	at com.google.api.client.googleapis.json.GoogleJsonResponseException.from(GoogleJsonResponseException.java:146)
	at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:113)
	at com.google.api.client.googleapis.services.json.AbstractGoogleJsonClientRequest.newExceptionOnError(AbstractGoogleJsonClientRequest.java:40)
	at com.google.api.client.googleapis.services.AbstractGoogleClientRequest$1.interceptResponse(AbstractGoogleClientRequest.java:321)
	at com.google.api.client.http.HttpRequest.execute(HttpRequest.java:1065)
	at com.google.api.client.googleapis.services.AbstractGoogleClientRequest.executeUnpars

I had thought it would be a real easy transition from DEV to QA & then to PROD, but now I'm struggling here in QA. 

Could you please help me with this? 

Using service account

Visitor ✭ ✭ ✭
# 5
Visitor ✭ ✭ ✭

Hi John, 

 

Were you able to resolve your issue using service account? I'm totally lost here, if I use my personal account then it works but with service account it doesn't. Also, I'm able to get "Location Categories" using the same account, so not sure why Create is having issues.