/** * Copyright 2015 ArcBees Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.arcbees.gaestudio.client.application.visualizer.widget; import java.util.ArrayList; import java.util.List; import java.util.Set; import com.arcbees.gaestudio.client.application.event.ColumnVisibilityChangedEvent; import com.arcbees.gaestudio.client.application.event.DisplayMessageEvent; import com.arcbees.gaestudio.client.application.event.RowLockedEvent; import com.arcbees.gaestudio.client.application.event.RowUnlockedEvent; import com.arcbees.gaestudio.client.application.visualizer.ParsedEntity; import com.arcbees.gaestudio.client.application.visualizer.columnfilter.ColumnFilterPresenter; import com.arcbees.gaestudio.client.application.visualizer.columnfilter.TypeInfoLoadedEvent; import com.arcbees.gaestudio.client.application.visualizer.event.DeselectEvent; import com.arcbees.gaestudio.client.application.visualizer.event.EntitiesDeletedEvent; import com.arcbees.gaestudio.client.application.visualizer.event.EntitiesSavedEvent; import com.arcbees.gaestudio.client.application.visualizer.event.EntitiesSelectedEvent; import com.arcbees.gaestudio.client.application.visualizer.event.EntityDeletedEvent; import com.arcbees.gaestudio.client.application.visualizer.event.EntityPageLoadedEvent; import com.arcbees.gaestudio.client.application.visualizer.event.EntitySavedEvent; import com.arcbees.gaestudio.client.application.visualizer.event.EntitySelectedEvent; import com.arcbees.gaestudio.client.application.visualizer.event.KindSelectedEvent; import com.arcbees.gaestudio.client.application.visualizer.event.NamespaceSelectedEvent; import com.arcbees.gaestudio.client.application.visualizer.event.SetStateFromPlaceRequestEvent; import com.arcbees.gaestudio.client.application.widget.message.Message; import com.arcbees.gaestudio.client.application.widget.message.MessageStyle; import com.arcbees.gaestudio.client.place.NameTokens; import com.arcbees.gaestudio.client.resources.AppConstants; import com.arcbees.gaestudio.client.resources.AppMessages; import com.arcbees.gaestudio.client.rest.EntitiesService; import com.arcbees.gaestudio.client.rest.GqlService; import com.arcbees.gaestudio.client.util.AsyncCallbackImpl; import com.arcbees.gaestudio.client.util.RestCallbackImpl; import com.arcbees.gaestudio.shared.dto.entity.AppIdNamespaceDto; import com.arcbees.gaestudio.shared.dto.entity.EntityDto; import com.arcbees.gaestudio.shared.rest.UrlParameters; import com.google.common.base.Function; import com.google.common.base.Joiner; import com.google.common.base.Splitter; import com.google.common.base.Strings; import com.google.common.collect.FluentIterable; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.gwt.http.client.Response; import com.google.gwt.view.client.AsyncDataProvider; import com.google.gwt.view.client.HasData; import com.google.gwt.view.client.Range; import com.google.inject.Inject; import com.google.web.bindery.event.shared.EventBus; import com.gwtplatform.dispatch.rest.client.RestDispatch; import com.gwtplatform.dispatch.rest.shared.RestAction; import com.gwtplatform.mvp.client.HasUiHandlers; import com.gwtplatform.mvp.client.PresenterWidget; import com.gwtplatform.mvp.client.View; import com.gwtplatform.mvp.client.proxy.PlaceManager; import com.gwtplatform.mvp.shared.proxy.PlaceRequest; import static com.arcbees.gaestudio.client.application.visualizer.event.EntitiesDeletedEvent.EntitiesDeletedHandler; import static com.arcbees.gaestudio.client.application.visualizer.event.EntityDeletedEvent.EntityDeletedHandler; import static com.arcbees.gaestudio.client.application.visualizer.event.EntitySavedEvent.EntitySavedHandler; public class EntityListPresenter extends PresenterWidget<EntityListPresenter.MyView> implements EntityListUiHandlers, EntitySavedHandler, EntityDeletedHandler, EntitiesDeletedHandler, SetStateFromPlaceRequestEvent.SetStateFromPlaceRequestHandler, KindSelectedEvent.KindSelectedHandler, EntitiesSavedEvent.EntitiesSavedHandler, DeselectEvent.DeselectHandler, NamespaceSelectedEvent.NamespaceSelectedHandler, ColumnVisibilityChangedEvent.ColumnVisibilityChangedHandler { interface MyView extends View, HasUiHandlers<EntityListUiHandlers> { void setNewKind(String currentKind); void setTableDataProvider(AsyncDataProvider<ParsedEntity> dataProvider); void setRowCount(Integer count); void setData(Range range, List<ParsedEntity> parsedEntities); void hideList(); void removeEntity(EntityDto entityDTO); void addProperty(String propertyName); void redraw(); void removeKindSpecificColumns(); void unselectRows(); void setRowSelected(String encodedKey); void blockSendingNewRequests(); void allowSendingNewRequests(); void addOrReplaceEntities(List<EntityDto> entities); void setKind(String appId, String namespace, String kind); void updateColumnVisibility(); } static final Object SLOT_COLUMN_CONFIG_TOOLTIP = new Object(); private final RestDispatch restDispatch; private final EntitiesService entitiesService; private final PlaceManager placeManager; private final PropertyNamesAggregator propertyNamesAggregator; private final GqlService gqlService; private final AppConstants appConstants; private final AppMessages appMessages; private final ColumnFilterPresenter columnFilterPresenter; private String currentKind; private String currentNamespace; private String currentGqlRequest; private Integer currentGqlNumberOfEntities = 0; private boolean isByGql; @Inject EntityListPresenter( EventBus eventBus, MyView view, PlaceManager placeManager, RestDispatch restDispatch, EntitiesService entitiesService, PropertyNamesAggregator propertyNamesAggregator, GqlService gqlService, AppConstants appConstants, AppMessages appMessages, ColumnFilterPresenter columnFilterPresenter) { super(eventBus, view); this.placeManager = placeManager; this.restDispatch = restDispatch; this.entitiesService = entitiesService; this.propertyNamesAggregator = propertyNamesAggregator; this.gqlService = gqlService; this.appConstants = appConstants; this.appMessages = appMessages; this.columnFilterPresenter = columnFilterPresenter; getView().setUiHandlers(this); setTableDataProvider(); } @Override public void onKindSelected(KindSelectedEvent event) { getView().unselectRows(); currentGqlRequest = ""; } @Override public void onEntitySelected(Set<ParsedEntity> selectedEntities) { if (selectedEntities.size() == 1) { ParsedEntity selectedEntity = Iterables.getOnlyElement(selectedEntities); EntitySelectedEvent.fire(this, selectedEntity); } else { EntitiesSelectedEvent.fire(this, selectedEntities); } revealEntityPlace(selectedEntities); } @Override public void onRowUnlock() { PlaceRequest placeRequest = new PlaceRequest.Builder(placeManager.getCurrentPlaceRequest()) .nameToken(NameTokens.visualizer) .without(UrlParameters.KEY) .build(); placeManager.revealPlace(placeRequest); RowUnlockedEvent.fire(this); } @Override public void refresh() { revealEntityPlace(Sets.<ParsedEntity>newHashSet()); } public void loadKind(String kind) { this.currentKind = kind; getView().setNewKind(currentKind); } public void hideList() { getView().hideList(); } @Override public void onEntitySaved(EntitySavedEvent event) { getView().addOrReplaceEntities(Lists.newArrayList(event.getEntityDto())); } @Override public void onEntitiesSaved(EntitiesSavedEvent event) { getView().addOrReplaceEntities(event.getEntities()); } @Override public void onEntityDeleted(EntityDeletedEvent event) { getView().removeEntity(event.getEntityDto()); } @Override public void onEntitiesDeleted(EntitiesDeletedEvent event) { loadKind(this.currentKind); } @Override public void setStateFromPlaceRequest(SetStateFromPlaceRequestEvent event) { setRowsSelectedFromPlaceRequest(event); } @Override public void onDeselectEntities(DeselectEvent event) { getView().unselectRows(); } @Override public void runGqlQuery(String gqlRequest) { if (requestHasNoSelect(gqlRequest)) { DisplayMessageEvent.fire(this, new Message(appConstants.missingSelectInRequest(), MessageStyle.ERROR)); return; } getView().blockSendingNewRequests(); currentGqlRequest = replaceQuotes(gqlRequest); restDispatch.execute(gqlService.getRequestCount(currentGqlRequest), new RestCallbackImpl<Integer>() { @Override public void onSuccess(Integer number) { if (number == 0) { DisplayMessageEvent.fire(this, new Message(appConstants.noEntitiesMatchRequest(), MessageStyle.ERROR)); showEntities(new ArrayList<EntityDto>(), new Range(0, 0), number); } else { DisplayMessageEvent.fire(this, new Message(appMessages.entitiesMatchRequest(number), MessageStyle.SUCCESS)); retrieveEntities(new Range(0, 25), number); currentGqlNumberOfEntities = number; } } @Override public void setResponse(Response response) { getView().allowSendingNewRequests(); int statusCode = response.getStatusCode(); if (statusCode == Response.SC_BAD_REQUEST) { DisplayMessageEvent.fire(this, new Message(appConstants.wrongGqlRequest(), MessageStyle.ERROR)); } else if (statusCode != Response.SC_OK) { DisplayMessageEvent.fire(this, new Message(appConstants.somethingWentWrong(), MessageStyle.ERROR)); } } }); } @Override public void setUseGql(boolean isByGql) { this.isByGql = isByGql; } @Override public void onNamespaceSelected(NamespaceSelectedEvent event) { currentNamespace = event.getNamespace(); } @Override public void onColumnVisibilityChanged(ColumnVisibilityChangedEvent event) { getView().updateColumnVisibility(); } @Override protected void onBind() { super.onBind(); addRegisteredHandler(EntitySavedEvent.getType(), this); addRegisteredHandler(EntitiesSavedEvent.getType(), this); addRegisteredHandler(EntityDeletedEvent.getType(), this); addRegisteredHandler(EntitiesDeletedEvent.getType(), this); addRegisteredHandler(SetStateFromPlaceRequestEvent.getType(), this); addRegisteredHandler(KindSelectedEvent.getType(), this); addRegisteredHandler(DeselectEvent.getType(), this); addRegisteredHandler(NamespaceSelectedEvent.getType(), this); addRegisteredHandler(ColumnVisibilityChangedEvent.getType(), this); setInSlot(SLOT_COLUMN_CONFIG_TOOLTIP, columnFilterPresenter); } private void showEntities(List<EntityDto> entities, Range range, Integer number) { setData(entities, range); getView().setRowCount(number); } private void setTableDataProvider() { AsyncDataProvider<ParsedEntity> dataProvider = new AsyncDataProvider<ParsedEntity>() { @Override protected void onRangeChanged(HasData<ParsedEntity> display) { loadPage(display); } }; getView().setTableDataProvider(dataProvider); } private void setTotalCount() { restDispatch.execute(getCountByKindAction(), new AsyncCallbackImpl<Integer>() { @Override public void onSuccess(Integer result) { getView().setRowCount(result); } }); } private RestAction<Integer> getCountByKindAction() { if (currentNamespace == null) { return entitiesService.getCountByKind(currentKind); } else { return entitiesService.getCountByKind(currentKind, currentNamespace); } } private void loadPage(final HasData<ParsedEntity> display) { if (currentKind == null) { display.setRowCount(0); } else { revealEntityPlace(Sets.<ParsedEntity>newHashSet()); final Range range = display.getVisibleRange(); if (isByGql) { retrieveEntities(range, currentGqlNumberOfEntities); } else { restDispatch.execute(getByKindAction(range), new AsyncCallbackImpl<List<EntityDto>>() { @Override public void onSuccess(List<EntityDto> result) { setData(result, range); setTotalCount(); } } ); } } EntityPageLoadedEvent.fire(this); } private RestAction<List<EntityDto>> getByKindAction(Range range) { if (currentNamespace == null) { return entitiesService.getByKind(currentKind, range.getStart(), range.getLength()); } else { return entitiesService.getByKind(currentKind, currentNamespace, range.getStart(), range.getLength()); } } private void setData(List<EntityDto> entities, Range range) { List<ParsedEntity> parsedEntities = transformToParsedEntities(entities); adjustColumns(parsedEntities); getView().setData(range, parsedEntities); } private void adjustColumns(List<ParsedEntity> entities) { getView().removeKindSpecificColumns(); if (!entities.isEmpty()) { Set<String> propertyNames = propertyNamesAggregator.aggregatePropertyNames(entities); for (String propertyName : propertyNames) { getView().addProperty(propertyName); } List<String> columnNames = Lists.newArrayList(ParsedEntityColumnCreator.DEFAULT_COLUMN_NAMES); columnNames.addAll(propertyNames); ParsedEntity prototype = entities.get(0); TypeInfoLoadedEvent.fire(this, columnNames, prototype); AppIdNamespaceDto appIdNamespace = prototype.getKey().getAppIdNamespace(); getView().setKind(appIdNamespace.getAppId(), appIdNamespace.getNamespace(), prototype.getKey().getKind()); } getView().redraw(); } private void revealEntityPlace(Set<ParsedEntity> parsedEntity) { List<String> keys = FluentIterable.from(parsedEntity) .transform(new Function<ParsedEntity, String>() { @Override public String apply(ParsedEntity input) { return input.getKey().getEncodedKey(); } }).toList(); String keysParam = Joiner.on(",").join(keys); PlaceRequest.Builder builder = new PlaceRequest.Builder(placeManager.getCurrentPlaceRequest()) .nameToken(NameTokens.visualizer); if (keys.isEmpty()) { RowUnlockedEvent.fire(this); builder.without(UrlParameters.KEY); } else { builder.with(UrlParameters.KEY, keysParam); } placeManager.revealPlace(builder.build()); } private List<ParsedEntity> transformToParsedEntities(List<EntityDto> entities) { List<ParsedEntity> parsedEntities = new ArrayList<>(); for (EntityDto entityDto : entities) { ParsedEntity parsedEntity = new ParsedEntity(entityDto); parsedEntities.add(parsedEntity); } return parsedEntities; } private boolean requestHasNoSelect(String gqlRequest) { return !gqlRequest.trim().toUpperCase().startsWith("SELECT"); } private String replaceQuotes(String gqlRequest) { return gqlRequest.replace("\"", "'"); } private void retrieveEntities(final Range range, final Integer numberOfEntities) { restDispatch.execute(gqlService.executeGqlRequest(currentGqlRequest, range.getStart(), range.getLength()), new AsyncCallbackImpl<List<EntityDto>>() { @Override public void onSuccess(List<EntityDto> result) { showEntities(result, range, numberOfEntities); } } ); } private void setRowsSelectedFromPlaceRequest(SetStateFromPlaceRequestEvent event) { PlaceRequest placeRequest = event.getPlaceRequest(); String encodedKeysParam = placeRequest.getParameter(UrlParameters.KEY, ""); if (!Strings.isNullOrEmpty(encodedKeysParam)) { List<String> encodedKeys = Splitter.on(",").splitToList(encodedKeysParam); if (encodedKeys.size() == 1) { RowLockedEvent.fire(this); } for (String encodedKey : encodedKeys) { getView().setRowSelected(encodedKey); } } } }