18F/cg-dashboard

View on GitHub
static_src/stores/route_store.js

Summary

Maintainability
C
1 day
Test Coverage
/*
 * Store for route data. Will store and update route data on changes from UI and
 * server.
 */

import Immutable from "immutable";

import BaseStore from "./base_store.js";
import DomainStore from "./domain_store.js";
import cfApi from "../util/cf_api.js";
import formatRoute from "../util/format_route.js";
import { routeActionTypes } from "../constants.js";

class RouteStore extends BaseStore {
  constructor() {
    super();
    this.storeData = new Immutable.List();
    this.showCreateRouteForm = false;
    this.error = null;
    this.subscribe(() => this.handleAction.bind(this));
  }

  getAllForSpace(spaceGuid) {
    return this.getAll().filter(route => route.space_guid === spaceGuid);
  }

  mergeRoutes(routes) {
    this.mergeMany("guid", routes, changed => {
      if (changed) this.emitChange();
      routes.forEach(route => {
        if (/shared_domains/.test(route.domain_url)) {
          cfApi.fetchSharedDomain(route.domain_guid);
        } else {
          cfApi.fetchPrivateDomain(route.domain_guid);
        }
      });
    });
  }

  isRouteBoundToApp(route) {
    return !!route.app_guid;
  }

  getRouteURLForApp(app) {
    const allRoutes = this.getAll();
    const boundRoutes = allRoutes.filter(route => route.app_guid === app.guid);
    let url;
    if (boundRoutes.length) {
      const route = boundRoutes[0];
      let mainRoute = Object.assign({}, route);
      const domain = DomainStore.get(route.domain_guid);
      if (domain) {
        mainRoute = Object.assign({}, mainRoute, { domain_name: domain.name });
      }
      if (route.path && route.path[0] === "/") {
        mainRoute.path = route.path.replace("/", "");
      }
      const { domain_name, host, path } = mainRoute;
      url = formatRoute(domain_name, host, path);
    }
    return url;
  }

  handleAction(action) {
    switch (action.type) {
      case routeActionTypes.ROUTES_RECEIVED: {
        const routes = action.routes;
        this.mergeRoutes(routes);
        break;
      }

      case routeActionTypes.ROUTE_APP_ASSOCIATE: {
        cfApi.putAppRouteAssociation(action.appGuid, action.routeGuid);
        const route = this.get(action.routeGuid);
        if (route) {
          const newRoute = Object.assign({}, route, { loading: "Binding" });
          this.merge("guid", newRoute, () => this.emitChange());
        }
        break;
      }

      case routeActionTypes.ROUTE_APP_ASSOCIATED: {
        const route = this.get(action.routeGuid);
        const newRoute = Object.assign({}, route, {
          app_guid: action.appGuid,
          editing: false,
          loading: false,
          error: null
        });
        this.merge("guid", newRoute, () => {
          this.showCreateRouteForm = false;
          this.error = null;
          this.emitChange();
        });
        break;
      }

      case routeActionTypes.ROUTE_APP_UNASSOCIATE: {
        cfApi.deleteAppRouteAssociation(action.appGuid, action.routeGuid);
        const route = this.get(action.routeGuid);
        if (route) {
          const newRoute = Object.assign({}, route, { loading: "Unbinding" });
          this.merge("guid", newRoute, () => this.emitChange());
        }
        break;
      }

      case routeActionTypes.ROUTE_APP_UNASSOCIATED: {
        const route = this.get(action.routeGuid);
        if (route) {
          const newRoute = Object.assign({}, route, {
            app_guid: null,
            loading: false,
            removing: false
          });
          this.merge("guid", newRoute, () => this.emitChange());
        }
        break;
      }

      case routeActionTypes.ROUTE_CREATE: {
        const { domainGuid, spaceGuid, host, path } = action;
        cfApi.createRoute(domainGuid, spaceGuid, host, path);
        break;
      }

      case routeActionTypes.ROUTE_CREATE_ERROR: {
        this.error = action.error;
        this.emitChange();
        break;
      }

      case routeActionTypes.ROUTE_CREATE_AND_ASSOCIATE: {
        this.emitChange();
        const { appGuid, domainGuid, spaceGuid } = action;
        const { host, path } = action.route;
        cfApi
          .createRoute(domainGuid, spaceGuid, host, path)
          .then(res => {
            const routeGuid = res.metadata.guid;
            cfApi.putAppRouteAssociation(appGuid, routeGuid);
          })
          .catch(() => {
            // Do nothing, error handled in cf_api.js
          });
        break;
      }

      case routeActionTypes.ROUTE_CREATE_FORM_HIDE: {
        this.showCreateRouteForm = false;
        this.error = null;
        this.emitChange();
        break;
      }

      case routeActionTypes.ROUTE_CREATE_FORM_SHOW: {
        this.showCreateRouteForm = true;
        this.error = null;
        this.emitChange();
        break;
      }

      case routeActionTypes.ROUTE_CREATED: {
        const route = action.route;
        this.merge("guid", route, () => this.emitChange());
        break;
      }

      case routeActionTypes.ROUTE_DELETE: {
        cfApi.deleteRoute(action.routeGuid);
        break;
      }

      case routeActionTypes.ROUTE_DELETED: {
        this.delete(action.routeGuid, () => {
          this.emitChange();
        });
        break;
      }

      case routeActionTypes.ROUTES_FOR_APP_FETCH:
        cfApi.fetchRoutesForApp(action.appGuid);
        break;

      case routeActionTypes.ROUTES_FOR_SPACE_FETCH:
        cfApi.fetchRoutesForSpace(action.spaceGuid);
        break;

      case routeActionTypes.ROUTES_FOR_APP_RECEIVED: {
        const routes = action.routes.map(route =>
          Object.assign({}, route, { app_guid: action.appGuid })
        );
        this.mergeRoutes(routes);
        break;
      }

      case routeActionTypes.ROUTE_TOGGLE_EDIT: {
        const route = this.get(action.routeGuid);
        const newRoute = Object.assign({}, route, {
          editing: !route.editing,
          error: null
        });
        this.merge("guid", newRoute, changed => {
          if (changed) this.emitChange();
        });
        break;
      }

      case routeActionTypes.ROUTE_TOGGLE_REMOVE: {
        const route = this.get(action.routeGuid);
        const newRoute = Object.assign({}, route, {
          removing: !route.removing
        });
        this.merge("guid", newRoute, changed => {
          if (changed) this.emitChange();
        });
        break;
      }

      case routeActionTypes.ROUTE_UPDATE: {
        const { routeGuid, domainGuid, spaceGuid, route } = action;
        cfApi.putRouteUpdate(routeGuid, domainGuid, spaceGuid, route);
        const currentRoute = this.get(routeGuid);
        if (currentRoute) {
          const newRoute = Object.assign({}, currentRoute, {
            loading: "Editing"
          });
          this.merge("guid", newRoute, () => this.emitChange());
        }
        break;
      }

      case routeActionTypes.ROUTE_UPDATED: {
        const route = Object.assign({}, action.route, {
          editing: false,
          error: null,
          loading: false
        });
        this.merge("guid", route, () => {
          this.emitChange();
        });
        break;
      }

      case routeActionTypes.ROUTE_ERROR: {
        const route = this.get(action.routeGuid);
        if (!route) break;
        const newRoute = Object.assign({}, route, {
          error: action.error,
          loading: null
        });
        this.merge("guid", newRoute, changed => {
          if (changed) this.emitChange();
        });
        break;
      }

      default:
        break;
    }
  }
}

export default new RouteStore();