1 /* 2 * Copyright (C) 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you 5 * may not use this file except in compliance with the License. You may 6 * obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 13 * implied. See the License for the specific language governing 14 * permissions and limitations under the License. 15 */ 16 17 package com.android.vts.job; 18 19 import com.android.vts.entity.ApiCoverageExcludedEntity; 20 import com.android.vts.entity.DashboardEntity; 21 import com.google.api.client.auth.oauth2.Credential; 22 import com.google.api.client.extensions.appengine.datastore.AppEngineDataStoreFactory; 23 import com.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp; 24 import com.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver; 25 import com.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow; 26 import com.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets; 27 import com.google.api.client.googleapis.javanet.GoogleNetHttpTransport; 28 import com.google.api.client.http.javanet.NetHttpTransport; 29 import com.google.api.client.json.JsonFactory; 30 import com.google.api.client.json.jackson2.JacksonFactory; 31 import com.google.api.services.sheets.v4.Sheets; 32 import com.google.api.services.sheets.v4.model.ValueRange; 33 34 import java.io.IOException; 35 import java.io.InputStream; 36 import java.io.InputStreamReader; 37 import java.security.GeneralSecurityException; 38 import java.util.ArrayList; 39 import java.util.List; 40 41 import javax.servlet.ServletConfig; 42 import javax.servlet.ServletException; 43 import javax.servlet.http.HttpServletRequest; 44 import javax.servlet.http.HttpServletResponse; 45 import java.io.IOException; 46 import java.util.logging.Level; 47 import java.util.logging.Logger; 48 49 /** Job to sync excluded API data in google spreadsheet with datastore's entity. */ 50 public class VtsSpreadSheetSyncServlet extends BaseJobServlet { 51 52 protected static final Logger logger = 53 Logger.getLogger(VtsSpreadSheetSyncServlet.class.getName()); 54 55 private static final String APPLICATION_NAME = "VTS Dashboard"; 56 private static final JsonFactory JSON_FACTORY = JacksonFactory.getDefaultInstance(); 57 58 private String CREDENTIALS_KEY_FILE = ""; 59 60 /** GoogleClientSecrets for GoogleAuthorizationCodeFlow Builder */ 61 private GoogleClientSecrets clientSecrets; 62 63 /** This is the ID of google spreadsheet. */ 64 private String SPREAD_SHEET_ID = ""; 65 66 /** This is the range to read of google spreadsheet. */ 67 private String SPREAD_SHEET_RANGE = ""; 68 69 @Override init(ServletConfig servletConfig)70 public void init(ServletConfig servletConfig) throws ServletException { 71 super.init(servletConfig); 72 73 try { 74 CREDENTIALS_KEY_FILE = systemConfigProp.getProperty("api.coverage.keyFile"); 75 SPREAD_SHEET_ID = systemConfigProp.getProperty("api.coverage.spreadSheetId"); 76 SPREAD_SHEET_RANGE = systemConfigProp.getProperty("api.coverage.spreadSheetRange"); 77 78 InputStream keyFileInputStream = 79 this.getClass() 80 .getClassLoader() 81 .getResourceAsStream("keys/" + CREDENTIALS_KEY_FILE); 82 InputStreamReader keyFileStreamReader = new InputStreamReader(keyFileInputStream); 83 84 this.clientSecrets = GoogleClientSecrets.load(JSON_FACTORY, keyFileStreamReader); 85 } catch (IOException ioe) { 86 logger.log(Level.SEVERE, ioe.getMessage()); 87 } catch (Exception exception) { 88 logger.log(Level.SEVERE, exception.getMessage()); 89 } 90 } 91 92 /** 93 * Creates an authorized Credential object. 94 * 95 * @param HTTP_TRANSPORT The network HTTP Transport. 96 * @param appEngineDataStoreFactory The credential will be persisted using the Google App Engine 97 * Data Store API. 98 * @param SCOPES Scopes are strings that enable access to particular resources, such as user 99 * data. 100 * @return An authorized Credential object. 101 * @throws IOException If the credentials.json file cannot be found. 102 */ getCredentials( final NetHttpTransport HTTP_TRANSPORT, final AppEngineDataStoreFactory appEngineDataStoreFactory, final List<String> SCOPES)103 private Credential getCredentials( 104 final NetHttpTransport HTTP_TRANSPORT, 105 final AppEngineDataStoreFactory appEngineDataStoreFactory, 106 final List<String> SCOPES) 107 throws IOException { 108 109 // Build flow and trigger user authorization request. 110 GoogleAuthorizationCodeFlow flow = 111 new GoogleAuthorizationCodeFlow.Builder( 112 HTTP_TRANSPORT, JSON_FACTORY, this.clientSecrets, SCOPES) 113 .setDataStoreFactory(appEngineDataStoreFactory) 114 .setAccessType("offline") 115 .build(); 116 LocalServerReceiver localServerReceiver = new LocalServerReceiver(); 117 return new AuthorizationCodeInstalledApp(flow, localServerReceiver).authorize("user"); 118 } 119 120 @Override doGet(HttpServletRequest request, HttpServletResponse response)121 public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { 122 123 try { 124 // Build a new authorized API client service. 125 final NetHttpTransport HTTP_TRANSPORT = GoogleNetHttpTransport.newTrustedTransport(); 126 final AppEngineDataStoreFactory appEngineDataStoreFactory = 127 (AppEngineDataStoreFactory) 128 request.getServletContext().getAttribute("dataStoreFactory"); 129 final List<String> googleApiScopes = 130 (List<String>) request.getServletContext().getAttribute("googleApiScopes"); 131 132 Sheets service = 133 new Sheets.Builder( 134 HTTP_TRANSPORT, 135 JSON_FACTORY, 136 getCredentials( 137 HTTP_TRANSPORT, 138 appEngineDataStoreFactory, 139 googleApiScopes)) 140 .setApplicationName(APPLICATION_NAME) 141 .build(); 142 143 ValueRange valueRange = 144 service.spreadsheets() 145 .values() 146 .get(SPREAD_SHEET_ID, SPREAD_SHEET_RANGE) 147 .execute(); 148 149 List<ApiCoverageExcludedEntity> apiCoverageExcludedEntities = new ArrayList<>(); 150 List<List<Object>> values = valueRange.getValues(); 151 if (values == null || values.isEmpty()) { 152 logger.log(Level.WARNING, "No data found in google spreadsheet."); 153 } else { 154 for (List row : values) { 155 ApiCoverageExcludedEntity apiCoverageExcludedEntity = 156 new ApiCoverageExcludedEntity( 157 row.get(0).toString(), 158 row.get(1).toString(), 159 row.get(2).toString(), 160 row.get(3).toString(), 161 row.get(4).toString()); 162 apiCoverageExcludedEntities.add(apiCoverageExcludedEntity); 163 } 164 } 165 166 DashboardEntity.saveAll(apiCoverageExcludedEntities, MAX_ENTITY_SIZE_PER_TRANSACTION); 167 168 } catch (GeneralSecurityException gse) { 169 logger.log(Level.SEVERE, gse.getMessage()); 170 } 171 } 172 } 173