1package autotest.moblab.wizard;
2
3import com.google.gwt.event.dom.client.ClickEvent;
4import com.google.gwt.event.dom.client.ClickHandler;
5import com.google.gwt.event.logical.shared.ValueChangeEvent;
6import com.google.gwt.event.logical.shared.ValueChangeHandler;
7import com.google.gwt.json.client.JSONObject;
8import com.google.gwt.user.client.ui.Anchor;
9import com.google.gwt.user.client.ui.CheckBox;
10import com.google.gwt.user.client.ui.FlowPanel;
11import com.google.gwt.user.client.ui.InlineLabel;
12import com.google.gwt.user.client.ui.Label;
13import com.google.gwt.user.client.ui.PopupPanel;
14import com.google.gwt.user.client.ui.TextBox;
15
16import autotest.common.Utils;
17import autotest.moblab.rpc.CloudStorageInfo;
18import autotest.moblab.rpc.MoblabRpcCallbacks;
19import autotest.moblab.rpc.MoblabRpcHelper;
20import autotest.moblab.rpc.OperationStatus;
21import autotest.common.ui.ToolTip;
22
23import java.util.HashMap;
24
25/**
26 * Wizard card for cloud storage configuration.
27 */
28public class CloudStorageCard extends FlexWizardCard {
29  /**
30   * The cached cloud storage information.
31   */
32  private CloudStorageInfo cloudStorageInfo;
33
34  /**
35   * Checkbox for if reuse existing boto file.
36   */
37  private CheckBox chkUseExisting;
38
39  public CloudStorageCard() {
40    super();
41    setViewTitle("Google Cloud Storage Configuration");
42    setEditTitle("Configure Access to Google Cloud Storage");
43  }
44
45  @Override
46  protected void updateModeUI() {
47    if (cloudStorageInfo != null) {
48      resetUI();
49      int row = 0;
50      // In edit mode, display the check box for re-using existing boto file.
51      if (ConfigWizard.Mode.Edit == getMode()) {
52        chkUseExisting = new CheckBox("Use Existing Boto File on Moblab Device.");
53        layoutTable.setWidget(row, 1, chkUseExisting);
54        chkUseExisting.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
55          @Override
56          public void onValueChange(ValueChangeEvent<Boolean> event) {
57            if (cloudStorageInfo != null) {
58              cloudStorageInfo.setUseExistingBotoFile(event.getValue());
59            }
60            TextBox box = getValueFieldEditor(CloudStorageInfo.JSON_FIELD_BOTO_KEY_ID);
61            if (box != null) {
62              box.setEnabled(!event.getValue());
63            }
64            box = getValueFieldEditor(CloudStorageInfo.JSON_FIELD_BOTO_SECRET_KEY);
65            if (box != null) {
66              box.setEnabled(!event.getValue());
67            }
68          }
69        });
70
71      }
72
73      // Row for boto key id.
74      row++;
75
76      // show a tooltip describing what a boto key is
77      FlowPanel botoKeyPanel = new FlowPanel();
78      botoKeyPanel.add(new InlineLabel("Boto Key ID "));
79      Anchor botoKeyHelp = new Anchor("?");
80      botoKeyHelp.addClickHandler(new ClickHandler() {
81        @Override
82        public void onClick(ClickEvent event) {
83          PopupPanel popup = new PopupPanel(true);
84          String helpText = "A boto key is used to access Google storage " +
85              "buckets. Please contact your Google PM for a boto key if you " +
86              "haven't received one already.";
87          popup.setWidget(new Label(helpText));
88          popup.showRelativeTo(layoutTable);
89        }
90      });
91      botoKeyPanel.add(botoKeyHelp);
92
93      layoutTable.setWidget(row, 0, botoKeyPanel);
94      layoutTable.setWidget(row, 1, createValueFieldWidget(CloudStorageInfo.JSON_FIELD_BOTO_KEY_ID,
95          cloudStorageInfo.getBotoKey()));
96
97      // Row for boto key secret.
98      row++;
99      layoutTable.setWidget(row, 0, new Label("Boto Key Secret"));
100      layoutTable.setWidget(row, 1, createStringValueFieldWidget(
101          CloudStorageInfo.JSON_FIELD_BOTO_SECRET_KEY, cloudStorageInfo.getBotoSecret(), true));
102
103      // Row for image storage bucket url.
104      row++;
105      layoutTable.setWidget(row, 0, new Label("Image Storage Bucket URL"));
106      String url = cloudStorageInfo.getImageStorageServer();
107      layoutTable.setWidget(row, 1,
108          createValueFieldWidget(CloudStorageInfo.JSON_FIELD_IMAGE_STORAGE_URL, url));
109      if (url != null && ConfigWizard.Mode.View == getMode()) {
110        Anchor link = Utils.createGoogleStorageHttpUrlLink("link", url);
111        layoutTable.setWidget(row, 2, link);
112      }
113
114      if (ConfigWizard.Mode.Edit == getMode()) {
115        chkUseExisting.setValue(cloudStorageInfo.isUseExistingBotoFile(), true);
116      }
117    } else {
118      MoblabRpcHelper.fetchCloudStorageInfo(new MoblabRpcCallbacks.FetchCloudStorageInfoCallback() {
119        @Override
120        public void onCloudStorageInfoFetched(CloudStorageInfo info) {
121          cloudStorageInfo = info;
122          updateModeUI();
123        }
124      });
125    }
126  }
127
128  @Override
129  public void validate(final CardValidationCallback callback) {
130    // If not use existing boto, than boto id and boto secret fields can not be empty.
131    if (!chkUseExisting.getValue()) {
132      cloudStorageInfo
133          .setBotoKey(getStringValueFieldValue(CloudStorageInfo.JSON_FIELD_BOTO_KEY_ID));
134      cloudStorageInfo
135          .setBotoSecret(getStringValueFieldValue(CloudStorageInfo.JSON_FIELD_BOTO_SECRET_KEY));
136    }
137    cloudStorageInfo.setImageStorageServer(
138          getStringValueFieldValue(CloudStorageInfo.JSON_FIELD_IMAGE_STORAGE_URL));
139    if (!chkUseExisting.getValue()) {
140      // Boto key and secret are required.
141      if (cloudStorageInfo.getBotoKey() == null || cloudStorageInfo.getBotoSecret() == null) {
142        callback.onValidationStatus(
143            new OperationStatus(false, "The boto key fields could not be empty"));
144        return;
145      }
146      // Image bucket and result bucket can not be empty.
147      if (cloudStorageInfo.getImageStorageServer() == null) {
148        callback.onValidationStatus(
149            new OperationStatus(false, "The image bucket URL fields could not be empty"));
150        return;
151      }
152    }
153    // Image bucket and result bucket must end in /.
154    if (cloudStorageInfo.getImageStorageServer().substring(
155      cloudStorageInfo.getImageStorageServer().length() - 1) != "/") {
156      callback.onValidationStatus(
157        new OperationStatus(false, "The image bucket URL must end in /"));
158      return;
159    }
160
161    // Sends validation request to server to validate the boto key and bucket urls.
162    MoblabRpcHelper.validateCloudStorageInfo(cloudStorageInfo,
163        new MoblabRpcCallbacks.ValidateCloudStorageInfoCallback() {
164          @Override
165          public void onCloudStorageInfoValidated(OperationStatus status) {
166            if (!status.isOk()) {
167              callback.onValidationStatus(status);
168              return;
169            }
170            CloudStorageCard.super.validate(callback);
171          }
172        });
173    return;
174  }
175
176  /**
177   * Gets the string input field value.
178   */
179  protected String getStringValueFieldValue(String fieldId) {
180    TextBox textBox = getValueFieldEditor(fieldId);
181    String value = textBox.getValue();
182    if (value != null) {
183      value = value.trim();
184    }
185
186    if (value == null || value.length() == 0) {
187      return null;
188    }
189    return value;
190  }
191
192  @Override
193  public void resetData() {
194    cloudStorageInfo = null;
195    super.resetData();
196  }
197
198  @Override
199  public void collectConfigData(@SuppressWarnings("unused") HashMap<String, JSONObject> map) {
200    if (map != null && cloudStorageInfo != null) {
201      map.put(MoblabRpcHelper.RPC_PARAM_CLOUD_STORAGE_INFO, cloudStorageInfo.toJson());
202    }
203  }
204}
205