rony2818 / react-native-boiler

0 stars 0 forks source link

Axios inteceptor #1

Open rony2818 opened 4 years ago

rony2818 commented 4 years ago
import axios, { AxiosError, AxiosInstance, AxiosPromise, AxiosRequestConfig, AxiosResponse, CancelStatic } from 'axios';
import Qs from 'qs';

import { WEBAPI_ROOT_URL } from '@/utils/constants';

interface AxiosRequestConfigEx extends AxiosRequestConfig {
  ex?: {
    start: number;
  };
}

// eslint-disable-next-line @typescript-eslint/no-empty-interface
interface AxiosErrorResponseEx
  extends AxiosResponse<{
    // 400 等の場合に設定される
    completeInfo?: {
      details: [{ message: string; row?: number }];
    };
    // 500 等の場合に設定され、404 等の場合は設定されない
    exception?: {
      error: string;
      message: string;
      trackingNumber?: string; // 503 等の場合は設定されない
    };
    error?: string;
    message?: string;
  }> {}

export interface AxiosErrorEx extends AxiosError {
  response?: AxiosErrorResponseEx; // サーバに接続できない場合やタイムアウトが発生した場合などでは設定されない
}

/**
 * `paramsSerializer` is an optional function in charge of serializing `params`
 * @see {@link https://github.com/axios/axios#request-config}
 */
const customConfig: AxiosRequestConfig = {
  baseURL: WEBAPI_ROOT_URL,
  timeout: 60000,
  paramsSerializer: (params) => {
    // Spring Boot 1.5.13にバージョンアップした際に必要となります。
    return Qs.stringify(params, { arrayFormat: 'brackets' });
  },
};

// 開発時はクッキーの送信を許可する
if (process.env.NODE_ENV === 'development') {
  customConfig.withCredentials = true;
}

const instance = axios.create(customConfig);

let csrfToken: string;
let errorHandler: (error: AxiosErrorEx) => void;
let preProcess: (config: AxiosRequestConfig) => Promise<AxiosRequestConfig>;
let postProcess: (config: AxiosResponse) => void;

// const createLogString = (prefix: 'start' | 'end', config: AxiosRequestConfig): string => {
//   return `${prefix.padEnd(5)} ${(config.method || '').padEnd(4)} ${(config.url || '').replace(WEBAPI_ROOT_URL, '')}`;
// };

instance.interceptors.request.use(
  async (config) => {
    (config as AxiosRequestConfigEx).ex = { start: new Date().getTime() };
    if (process.env.NODE_ENV === 'development') {
      // console.log(`[axios-ex] ${createLogString('start', config)}`);
    }
    const requestConfig = preProcess ? await preProcess(config) : await Promise.resolve(config);
    requestConfig.headers['X-Token'] = csrfToken || '';
    return requestConfig;
  },
  (error) => {
    return Promise.reject(error);
  }
);

instance.interceptors.response.use(
  (response) => {
    // const config: AxiosRequestConfigEx = response.config || { ex: { start: NaN } };
    // const start = config.ex ? config.ex.start : NaN;
    // const end = new Date().getTime();
    if (process.env.NODE_ENV === 'development') {
      // console.log(`[axios-ex] ${createLogString('end', config)} ${end - start}ms`, response);
    }
    if (postProcess) {
      postProcess(response);
    }
    return response;
  },
  (error) => {
    // const config = error.config || { ex: { start: NaN } };
    if (process.env.NODE_ENV === 'development') {
      // console.error(`[axios-ex] ${createLogString('end', config)}`, JSON.parse(JSON.stringify(error)));
    }
    if (postProcess) {
      postProcess(error);
    }
    if (errorHandler) {
      errorHandler(error);
    }
    return Promise.reject(error);
  }
);

export const helpers = {
  /**
   * axiosのインスタンスを返却します。
   *
   * @return axiosのインスタンス
   */
  getInstance(): AxiosInstance {
    return instance;
  },

  /**
   * axiosのインターセプター内でエラー時に動作する関数を設定します。
   *
   * @param callback コールバック
   */
  setErrorHandler(callback: (error: AxiosErrorEx) => void): void {
    errorHandler = callback;
  },

  /**
   * axiosでのリクエスト開始時に動作する関数を設定します。
   *
   * @param callback コールバック
   */
  setPreProcess(callback: (config: AxiosRequestConfig) => Promise<AxiosRequestConfig>): void {
    preProcess = callback;
  },

  /**
   * axiosでのレスポンス受信後に動作する関数を設定します。
   *
   * @param callback コールバック
   */
  setPostProcess(callback: (config: AxiosResponse) => void): void {
    postProcess = callback;
  },

  /**
   * CSRFトークンを設定します。
   *
   * @param token CSRFトークン
   */
  setCsrfToken(token: string): void {
    csrfToken = token;
  },

  /**
   * Cancel all pending requests.
   *
   * @param error Error
   * @return CancelStatic
   * @see {@link https://github.com/mzabriskie/axios#instance-methods}
   */
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  cancel(error: any): CancelStatic {
    throw new axios.Cancel(error);
  },
};

const methods = {
  /**
   * request.
   * @param config Config
   * @return Promise
   * @see {@link https://github.com/mzabriskie/axios#instance-methods}
   */
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  request(_config?: AxiosRequestConfig): AxiosPromise<any> {
    throw new Error('Not Implemented');
  },

  /**
   * get.
   * @param url Url
   * @param config Config
   * @return Promise
   * @see {@link https://github.com/mzabriskie/axios#instance-methods}
   */
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  get(url: string, config?: AxiosRequestConfig): AxiosPromise<any> {
    return instance.get(url, config);
  },

  /**
   * delete.
   * @param url Url
   * @param config Config
   * @return Promise
   * @see {@link https://github.com/mzabriskie/axios#instance-methods}
   */
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  delete(url: string, config?: AxiosRequestConfig): AxiosPromise<any> {
    return instance.delete(url, config);
  },

  /**
   * head.
   * @param url Url
   * @param config Config
   * @return Promise
   * @see {@link https://github.com/mzabriskie/axios#instance-methods}
   */
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  head(_url: string, _config?: AxiosRequestConfig): AxiosPromise<any> {
    throw new Error('Not Implemented');
  },

  /**
   * options.
   * @param url Url
   * @param config Config
   * @return Promise
   * @see {@link https://github.com/mzabriskie/axios#instance-methods}
   */
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  options(_url: string, _config?: AxiosRequestConfig): AxiosPromise<any> {
    throw new Error('Not Implemented');
  },

  /**
   * post.
   * @param url Url
   * @param data Data
   * @param config Config
   * @return Promise
   * @see {@link https://github.com/mzabriskie/axios#instance-methods}
   */
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  post(url: string, data?: any, config?: AxiosRequestConfig): AxiosPromise<any> {
    return instance.post(url, data, config);
  },

  /**
   * put.
   * @param url Url
   * @param data Data
   * @param config Config
   * @return Promise
   * @see {@link https://github.com/mzabriskie/axios#instance-methods}
   */
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  put(_url: string, _data?: any, _config?: AxiosRequestConfig): AxiosPromise<any> {
    throw new Error('Not Implemented');
  },

  /**
   * patch.
   * @param url Url
   * @param data Data
   * @param config Config
   * @return Promise
   * @see {@link https://github.com/mzabriskie/axios#instance-methods}
   */
  /* eslint-disable-next-line @typescript-eslint/no-explicit-any */
  patch(_url: string, _data?: any, _config?: AxiosRequestConfig): AxiosPromise<any> {
    throw new Error('Not Implemented');
  },
};

export default methods;
rony2818 commented 4 years ago
import { ContractKbn } from '@/utils/constants';

export interface LoginUser {
  companycode: string;
  userid: string;
  usercode: string;
  username: string;
  usermail: string;
  departmentcodes: string[];
  department?: {
    departmentCd: string;
    departmentNm: string;
  };
  subDepartmentcodes: string[][];
  authorities: Array<{ authority: string }>;
  adminFlg: boolean;
  locale: string;
  contractKbn?: ContractKbn;
  tenantCd: string;
  notInitializeFlg: boolean;
  notLoginFlg: boolean;
  countDateTrial: number;
  zendeskSubdomain: string;
  zendeskJwt: string;
  zendeskWidgetKey: string;
  slackClientId: string;
  introduction?: {
    globalMenuFlg: boolean;
    checkinFlg: boolean;
  };
  _csrf: string;
}

interface SimpleUserInfo {
  tenantId: string;
  employeeId: string;
  systemDate?: string;
}

/**
 * 正常終了のステータスかどうかをチェックします。
 *
 * @param status ステータス
 * @return true:正常、false:異常
 */
export const isValidStatus = (status: number): boolean => {
  return status >= 200 && status < 300; // axios default
};

/**
 * Get login user's information.
 *
 * @param disableErrorHandler true:disable error handler, false:default
 * @return Promise
 */
const findUserInformation = (disableErrorHandler: boolean): Promise<LoginUser> => {
  return axios
    .get('/auth/userInfo', {
      validateStatus(status: number): boolean {
        // https://github.com/mzabriskie/axios#handling-errors
        if (disableErrorHandler) {
          return true; // axios-exのエラーハンドラーを無効化するため、全てのステータスを正常系扱いにする
        } else {
          return isValidStatus(status);
        }
      }
    })
    .then(response => {
      if (isValidStatus(response.status)) {
        return response.data;
      } else {
        return Promise.reject(response);
      }
    });
};

/**
 * ログイン認証を行います。
 *
 * @param data ログイン情報
 * @return Promise
 */
const login = (data: {
  tenantCd: string;
  employeeMail: string;
  password: string;
  saveCredentials: boolean;
}): Promise<LoginUser> => {
  const params = new URLSearchParams();
  params.append('tenantcd', data.tenantCd);
  params.append('employeemail', data.employeeMail);
  params.append('password', data.password);
  params.append('savecredentials', data.saveCredentials + '');
  return axios
    .post('/auth/login/confirm', params, {
      validateStatus(status: number): boolean {
        // https://github.com/mzabriskie/axios#handling-errors
        return isValidStatus(status) || status === 401; // axios-exのエラーハンドラーを無効化するため、401を正常系扱いにする
      }
    })
    .then(response => {
      if (response.status === 401) {
        // 401を正常終了で受け取った後、サービスとしては異常終了とする
        return Promise.reject(response);
      } else {
        return response.data;
      }
    });
};

/**
 * ログアウトを行います。
 *
 * @return Promise
 */
const logout = (): Promise<string> => {
  return axios.post('/auth/logout').then(response => {
    return response.data;
  });
};

/**
 * ユーザのログイン情報を取得します。
 */
const bindLoginCredentials = (): Promise<{ tenantCd: string; employeeMail: string; saveCredentials: boolean }> => {
  return axios.get('/auth/credentials').then(response => {
    return response.data;
  });
};

/**
 * メールアドレス検索し、検索結果のメールアドレスにメール送信します。
 */
const sendPasswordResetEmail = (credentials: { tenantCd: string; employeeMail: string }): Promise<void> => {
  return axios.post('/auth/sendMail', credentials).then(response => {
    return response.data;
  });
};

// /**
//  * テナントIDとURLハッシュ検索し、検索結果のユーザーにパスワード再設定画面を表示する。
//  *
//  * @param data 社員データオブジェクト
//  * @param data.employeeMail メールアドレス
//  * @param data.tenantId テナントID
//  * @return Promise
//  */
// export function authenticateUrl(data) {
//  return axios.post('/auth/sendMail/url', data).then(response => {
//   return response.data;
//  });
// }

/**
 * 新規パスワードを設定します。
 */
const modifyPassword = (data: { password: string; tenantCd: string; urlHash: string }): Promise<void> => {
  return axios.post('/auth/password/modify', data).then(response => {
    return response.data;
  });
};

// /**
//  * 管理者情報の初期設定を行います。
//  *
//  * @param data 管理者情報オブジェクト
//  * @return Promise
//  */
// export function initialize(data) {
//  return axios.post('/auth/initialize', data).then(response => {
//   return response.data;
//  });
// }

/**
 * Get login url environment
 *
 * @param tenantCd テナントCD
 * @return Promise
 */
const searchLoginEnvironment = (tenantCd: string): Promise<string> => {
  return axios.get('/auth/loginEnvironment', { params: { tenantCd } }).then(response => {
    return response.data;
  });
};

/**
 * Generate zendesk Jwt
 *
 * @return {Promise.<String>} jwt
 */
const generateZendeskJwt = (): Promise<string> => {
  return axios.get('/auth/generateZendeskJwt').then(response => {
    return response.data;
  });
};

/**
 * Check if session is alive
 *
 * @param params params
 * @return {Promise.<Object>}
 */
const findSimpleUserInformation = (
  tenantId: string,
  employeeId: string,
  date?: string
): Promise<Partial<SimpleUserInfo>> => {
  return axios.post(`/auth/simpleUserInfo/${tenantId}/${employeeId}`, { date }).then(response => {
    return Object.entries(response.data).reduce((acc, [key, val]) => (val ? { [key]: val, ...acc } : acc), {});
  });
};

export default {
  findUserInformation,
  login,
  logout,
  bindLoginCredentials,
  sendPasswordResetEmail,
  // authenticateUrl
  modifyPassword,
  // initialize
  searchLoginEnvironment,
  generateZendeskJwt,
  findSimpleUserInformation
};
rony2818 commented 4 years ago

import { Base, Coin, CoinComment, Page, RegisterCoin, RegisterCoinComment } from '@/service/interfaces';
import axios from '@/utils/axios-ex';

export interface CoinReaction extends Partial<Base> {
  coinReactionId: string;
  coinId: string;
  employeeId: string;
}

export interface CoinDetail {
  coinDetails: {
    coinsReceived: Coin[];
    coinsSent: Coin[];
  };
}

/**
 * Send coin to employees
 *
 * @param  coinInfo coin's information
 * @return  Promise
 */
const sendCoinToEmployees = (coinInfo: RegisterCoin): Promise<Coin> => {
  return axios.post('/coin', coinInfo).then(response => {
    return response.data;
  });
};

// /**
//  * Search coins summary for department
//  *
//  * @param departmentCds department codes
//  * @return Promise
//  */
// const searchDepartmentCoins = (departmentCds: string[]): Promise<Page<Coin>> => {
//   const params = {
//     departmentCds
//   };
//   return axios
//     .get('/coin', {
//       params
//     })
//     .then(response => {
//       return response.data;
//     });
// };

// /**
//  * Search coins summary for employees
//  *
//  * @param employeeIds 社員ID
//  * @return Promise
//  */
// const searchSummary = (employeeIds: string[]): Promise<Page<Coin>> => {
//   const params = {
//     employeeIds
//   };
//   return axios
//     .get('/coin', {
//       params
//     })
//     .then(response => {
//       return response.data;
//     });
// };

/**
 * Search coins detail for employee.
 *
 * @param employeeId employee id
 * @param fromDate Date
 * @param toDate Date
 * @return Promise
 */
const searchDetail = (employeeId = '', fromDate?: string, toDate?: string): Promise<CoinDetail> => {
  const params = {
    employeeId,
    fromDate,
    toDate
  };
  return axios
    .get('/coin/detail', {
      params
    })
    .then(response => {
      return response.data;
    });
};

/**
 * コインIDからコインを取得します。
 *
 * @param coinId コインID
 * @return 取得結果
 */
const searchDetailByCoinId = (coinId: string): Promise<Coin> => {
  return axios.get(`/coin/${coinId}`).then(response => {
    return response.data;
  });
};

// /**
//  * Select Coin Report By DepartmentCd And Date.
//  *
//  * @param departmentCd departmentCd
//  * @param fromDate Date
//  * @param toDate Date
//  * @return Promise
//  */
// const searchCoinReport = (departmentCd: string, fromDate: Date, toDate: Date): Promise<Page<Coin>> => {
//   const params = {};
//   if (!_.isEmpty(departmentCd)) {
//     params.departmentCd = departmentCd;
//   }

//   if (!_.isEmpty(fromDate)) {
//     params.fromDate = fromDate;
//   }
//   if (!_.isEmpty(toDate)) {
//     params.toDate = toDate;
//   }
//   return axios
//     .get('/coin/report', {
//       params
//     })
//     .then(response => {
//       return response.data.content;
//     });
// };

/**
 * Remove coin info
 *
 * @param coinId Coin id
 * @return Promise
 */
const removeCoin = (coinId: string): Promise<Coin> => {
  return axios.delete(`/coin/${coinId}`).then(response => {
    return response.data;
  });
};

/**
 * Edit coin info
 *
 * @param coinId Coin id
 * @param coin coin's information
 * @return Promise
 */
const modifySentCoin = (coinId: string, coin: Coin): Promise<Coin> => {
  return axios.post(`/coin/${coinId}`, coin).then(response => {
    return response.data;
  });
};

/**
 * 指定したコイン反応に紐付く社員情報を取得します。
 *
 * @param coinId コインID
 * @return 取得結果
 */
const searchReactionEmployee = (
  coinId: string
): Promise<Page<{ employeeId: string; employeeLastNm: string; employeeFirstNm: string }>> => {
  return axios.get(`/coin/${coinId}/reaction`).then(response => {
    return response.data;
  });
};

/**
 * コイン反応を登録します。
 *
 * @param coinId コインID
 * @return 登録結果
 */
const registerReaction = (coinId: string): Promise<CoinReaction> => {
  return axios.post(`/coin/${coinId}/reaction`).then(response => {
    return response.data;
  });
};

/**
 * コイン反応を削除します。
 *
 * @param coinId コインID
 * @param employeeId 社員ID
 * @return 削除結果
 */
const removeReaction = (coinId: string): Promise<CoinReaction> => {
  return axios.delete(`/coin/${coinId}/reaction`).then(response => {
    return response.data;
  });
};

/**
 * コインコメントを登録します。
 *
 * @param coinId コインID
 * @param coinComment コインコメントの情報を保持するオブジェクト
 * @return 登録結果
 */
const registerComment = (coinId: string, coinComment: RegisterCoinComment): Promise<CoinComment> => {
  return axios.post(`/coin/${coinId}/comment`, coinComment).then(response => {
    return response.data;
  });
};

// /**
//  * コインコメントを更新します。
//  *
//  * @param coinId コインID
//  * @param coinCommentId コインコメントID
//  * @param coinComment コインコメントの情報を保持するオブジェクト
//  * @return 登録結果
//  */
// const modifyComment = (coinId: string, coinCommentId: string, coinComment: CoinComment): Promise<CoinComment> => {
//   return axios.post(`/coin/${coinId}/comment/${coinCommentId}`, coinComment).then(response => {
//     return response.data;
//   });
// };

/**
 * コインコメントを削除します。
 *
 * @param coinId コインID
 * @param coinCommentId コインコメントID
 * @return 削除結果
 */
const removeComment = (coinId: string, coinCommentId: string): Promise<CoinComment> => {
  return axios.delete(`/coin/${coinId}/comment/${coinCommentId}`).then(response => {
    return response.data;
  });
};

export default {
  sendCoinToEmployees,
  //   searchDepartmentCoins,
  searchDetailByCoinId,
  //   searchSummary,
  searchDetail,
  //   searchCoinReport,
  removeCoin,
  modifySentCoin,
  searchReactionEmployee,
  registerReaction,
  removeReaction,
  registerComment,
  // modifyComment,
  removeComment
};```
rony2818 commented 4 years ago

export interface Page<T> {
  content: T[];
  first: boolean;
  last: boolean;
  number: number;
  numberOfElements: number;
  size: number;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  sort: any;
  totalElements: number;
  totalPages: number;
}

export interface Base {
  insertDateTime: string;
  updateDateTime: string;
  versionNo: number;
}

export interface BaseCoin extends Partial<Base> {
  senderId: string;
  type: CoinType;
  privateFlg: 0 | 1;
  message: string;
}
export interface Coin extends BaseCoin {
  coinId: string;
  receivers: string[];
}

export interface RegisterCoin extends BaseCoin {
  receiverEmployeeIds: string[];
}

export interface CoinComment extends Partial<Base> {
  coinCommentId: string;
  coinId: string;
  employeeId: string;
  privateFlg: boolean;
  comment: string;
}

export interface RegisterCoinComment {
  privateFlg: boolean;
  comment: string;
}

export interface Department {
  departmentId: string;
  departmentCd: string;
  departmentNm: string;
}

export interface Employee {
  basic: {
    employeeId: string;
    employeeCd: string;
    employeeFullNm: string;
  };
  belongingInformation: Array<{
    belonging: string;
    currentFlg: boolean;
  }>;
}
rony2818 commented 4 years ago
import _ from 'lodash';
import Vue from 'vue';
import { Route } from 'vue-router';

import customFilters from '@/custom-filters';
import customMethods from '@/plugins/methods';
import veeValidate from '@/plugins/vee-validate';
import { LoginRouteName, RouteName as PermittedRoute } from '@/router/permitted-route';
import { RouteName as SlackRoute } from '@/router/slack-route';
import authService from '@/service/auth-service';
import { Action as AuthAction, AuthState, Getter as AuthGetter, LoginUser } from '@/store/modules/auth/types';
import { Action as CacheAction, EmployeeItemTab, Getter as CacheGetter } from '@/store/modules/cache/types';
import { Action, Mutation as RootMutation } from '@/store/types';
import { AxiosErrorEx, helpers } from '@/utils/axios-ex';
import * as stringUtil from '@/utils/string-util';

const DISABLE_LOADING_URLS = ['/functionLock/lock', '/auth/simpleUserInfo'];

export default class MainVue extends Vue {
  public lastConnectionDateTime?: string;

  public requestResolvers: Array<() => void> = [];
  public requestRejecters: Array<() => void> = [];

  public get loginUser(): LoginUser {
    return (this.$store.state.auth as AuthState).loginUser;
  }

  public get loginDialogState(): { action: 'resolve' | 'reject' | 'none'; isOpen: boolean } {
    return this.$store.state.loginDialog;
  }

  public get message(): { [index: string]: string } {
    return this.$store.getters[`cache/${CacheGetter.Message}`];
  }

  public get multilingual(): { [index: string]: string } {
    return this.$store.getters[`cache/${CacheGetter.Multilingual}`];
  }

  public get isLoggedIn(): boolean {
    return this.$store.getters[`auth/${AuthGetter.LoggedInFlg}`];
  }

  public get employeeItemSettings(): EmployeeItemTab[] {
    return this.$store.getters[`cache/${CacheGetter.EmployeeItemSettings}`];
  }

  public get title(): string {
    const title = `${_.get(
      this.multilingual,
      _.get(this.currentRoute, 'meta.titleKey'),
      _.get(this.currentRoute, 'meta.title', '')
    )}`;
    const suffix = `${_.get(this.multilingual, 'pageTitleSuffix', '- パフォーマンス -')}`;
    return `${title} ${suffix}`;
  }

  public get currentRoute(): Route {
    // When app is created, current route is root route,
    // actual route (the route that match location) is pending
    return this.$router.history.pending || this.$route;
  }

  public hasAuthority(functionCds: string | string[]): boolean {
    return this.$store.getters[`auth/${AuthGetter.HasAuthority}`](functionCds);
  }

  public findEnvironmentKbn(): Promise<void> {
    return this.$store.dispatch(Action.FindEnvironmentKbn);
  }

  public synchronizeMessage(): Promise<void> {
    return this.$store.dispatch(`cache/${CacheAction.SynchronizeMessage}`);
  }

  public synchronizeMultilingual(): Promise<void> {
    return this.$store.dispatch(`cache/${CacheAction.SynchronizeMultilingual}`);
  }

  public synchronizeCode(): Promise<void> {
    return this.$store.dispatch(`cache/${CacheAction.SynchronizeCode}`);
  }

  public synchronizeItems(): Promise<void> {
    return this.$store.dispatch(`cache/${CacheAction.SynchronizeEmployeeItemData}`);
  }

  public findUserInformation(params: { ignoreError: true }): Promise<void> {
    return this.$store.dispatch(`auth/${AuthAction.FindUserInformation}`, params);
  }

  public setRestError(error: AxiosErrorEx): void {
    this.$store.commit(`${RootMutation.SetRestError}`, error);
  }

  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  public setError(error: any): void {
    this.$store.commit(`${RootMutation.SetError}`, error);
  }

  public clearError(): void {
    this.$store.commit(`${RootMutation.ClearError}`);
  }

  public setLoginDialogState({ action, isOpen }: { action?: 'resolve' | 'reject' | 'none'; isOpen?: boolean }): void {
    this.$store.commit(`${RootMutation.SetLoginDialogState}`, { action, isOpen });
  }

  //        ...mapMutations('userSetting', {
  //            initUserSetting: types.userSetting.mutations.INIT_SETTING
  //        })

  public checkRouteAuthority(to: Route): boolean {
    const requiredAuthorities = _.get(to, 'meta.authorities', []);
    if (this.hasAuthority(requiredAuthorities)) {
      return true;
    }
    this.setError({ errorTitle: 'Forbidden', errorMessage: this.message.EKITEZ2002, status: 403 });
    return false;
  }

  public synchronizeCache(): Promise<void[]> {
    const promises = [];
    promises.push(this.synchronizeMessage());
    promises.push(this.synchronizeMultilingual());
    if (this.isLoggedIn) {
      promises.push(this.synchronizeCode());
      promises.push(this.synchronizeItems());
    }
    return Promise.all(promises);
  }

  public resolveAndClear(action: 'resolve' | 'reject'): void {
    if (action === 'resolve') {
      this.requestResolvers.forEach(resolve => resolve());
    } else {
      this.requestRejecters.forEach(reject => reject());
    }

    this.requestResolvers = [];
    this.requestRejecters = [];
  }

  public isLoaderRequest(requestConfig?: AxiosRequestConfig): boolean {
    // リクエストをキャンセルすると requestConfig は undefined となる(再ログインダイアログを表示し、ログインせずログアウトした場合)
    if (requestConfig === undefined) {
      return false;
    }
    const requestUrl = requestConfig.url || '';
    const baseURL = requestConfig.baseURL || '';
    const relativeUrl = requestUrl.replace(baseURL, '');
    return !DISABLE_LOADING_URLS.find(url => relativeUrl.includes(url));
  }

  public restErrorHandler(error: AxiosErrorEx): void {
    const response = error.response;
    if (response && [400, 409, 413].includes(response.status)) {
      this.$snackbar.hideAll();
      const defaultMessage = this.message.EKITEZ1001 || '入力パラメータに誤りがあるため処理できません。'; // アノテーションでバリデーションエラーが発生した場合はこれが適用される
      const rowMessage = this.message.EKITEZ1152 || '{0}行:{1}'; // 複数行エラーメッセージ
      if (response.data.completeInfo) {
        const details = response.data.completeInfo.details;
        const messages = details.map(detail =>
          detail.row ? stringUtil.format(rowMessage, detail.row, detail.message) : detail.message
        );
        this.$snackbar.error(messages);
      } else {
        this.$snackbar.error(defaultMessage);
      }
    } else {
      if (error instanceof axios.Cancel) {
        this.$router.push({ name: PermittedRoute.Logout, params: { logoutFlg: true + '', force: true + '' } }); // force: true でナビゲーションガードを無効にします
      } else {
        this.setRestError(error);
        this.$router.push({ name: PermittedRoute.Error });
      }
    }
  }

  public async restPreProcess(requestConfig: AxiosRequestConfig): Promise<AxiosRequestConfig> {
    if (this.isLoaderRequest(requestConfig)) {
      if (requestConfig.method && requestConfig.method.toUpperCase() === 'POST') {
        this.$loader.show();
      } else {
        this.$loader.showProgress();
      }
    }

    const { baseURL, url } = requestConfig;
    const isAuthRequest = `${baseURL}${url}`.includes(`${baseURL}/auth/`);

    if (this.isLoggedIn && !isAuthRequest) {
      try {
        const { tenantId, employeeId, systemDate } = await authService.findSimpleUserInformation(
          this.loginUser.tenantId,
          this.loginUser.employeeId,
          this.lastConnectionDateTime
        );
        this.lastConnectionDateTime = systemDate;

        if (!tenantId && !employeeId) {
          if (!this.loginDialogState.isOpen) {
            this.setLoginDialogState({ isOpen: true });
          }
          await new Promise((resolve, reject) => {
            this.requestResolvers = [...this.requestResolvers, resolve];
            this.requestRejecters = [...this.requestRejecters, reject];
          });
        }
      } catch (error) {
        helpers.cancel(error);
      }
    }

    return requestConfig;
  }

  public restPostProcess(response: AxiosResponse): void {
    if (this.isLoaderRequest(response.config)) {
      if (response.config.method && response.config.method.toUpperCase() === 'POST') {
        this.$loader.hide();
      } else {
        this.$loader.hideProgress();
      }
    }
  }

  /**
   * 初期表示のURLや認証状態に応じてログイン画面へ遷移します。
   *
   * @returns 初期表示のURLがログイン画面でなく、ログイン画面へ遷移した場合は true を返却する
   */
  public redirectToLogin(): boolean {
    if (this.isLoggedIn) {
      return false;
    }

    // ログイン画面へアクセスした場合、UserAgentを判別しPC・モバイルの適切なログイン画面へ遷移する
    if (this.currentRoute.name === LoginRouteName.Default || this.currentRoute.name === LoginRouteName.Mobile) {
      this.$router.push({ name: PermittedRoute.Login });
      return false;
    }

    // ログイン画面を除いて、認証前でもアクセスできる画面では何もしない
    const permittedPaths = [
      PermittedRoute.Root,
      PermittedRoute.Logout,
      PermittedRoute.Error,
      PermittedRoute.ForgotPassword,
      PermittedRoute.ResetPassword,
      // PermittedRoute.SignUp,
      PermittedRoute.TenantConfirmation,
      SlackRoute.AddToSlack
    ];
    if (permittedPaths.some(path => path === this.currentRoute.name)) {
      return false;
    }

    // 認証しておらず、認証後にアクセスできる画面の場合、ログイン画面へ遷移しつつ、ユーザがアクセスした画面の情報を保持する
    this.$router.push({
      name: PermittedRoute.Login,
      query: { 'return-to': window.location.href.replace(window.location.origin, '') }
    });
    return true;
  }

  public onLoginDialogStateChange(): void {
    const { action, isOpen } = this.loginDialogState;
    if (isOpen === false && (action === 'resolve' || action === 'reject')) {
      this.resolveAndClear(action);
      this.setLoginDialogState({ action: 'none' });
    }
  }

  public async onCreated(): Promise<void> {
    // Restクライアントでエラーが起こった場合の処理を設定
    helpers.setErrorHandler(error => {
      this.restErrorHandler(error);
    });
    // リクエスト前処理を設定
    helpers.setPreProcess(
      (config: AxiosRequestConfig): Promise<AxiosRequestConfig> => {
        return this.restPreProcess(config);
      }
    );
    // リクエスト後処理を設定
    helpers.setPostProcess(response => {
      return this.restPostProcess(response);
    });
    // ページ遷移前の処理の登録
    // ルートマッチ時の共通前処理(グローバルガード)
    this.$router.beforeEach(async (to, from, next) => {
      if (process.env.NODE_ENV === 'development') {
        // eslint-disable-next-line no-console
        console.log('[main] $router.beforeEach from', from);
        // eslint-disable-next-line no-console
        console.log('[main] $router.beforeEach to', to);
      }

      this.$snackbar.hideAll();

      if (!this.checkRouteAuthority(to)) {
        if (this.isLoggedIn) {
          next({ name: PermittedRoute.Error });
        } else {
          next({
            name: PermittedRoute.Login,
            query: { 'return-to': window.location.href.replace(window.location.origin, '') }
          });
        }
        return;
      }
      if (to.name === PermittedRoute.Logout || to.name === PermittedRoute.Error) {
        // ログアウトページ、エラーページへ遷移するときはすぐに遷移する(遷移処理中の想定外エラーを避けるため)
        next();
        return;
      }

      // 先頭の'/'を削除する。かつ、モバイル版の場合先頭pathに'm/'が付くため、そのpathも削除する。
      // replace処理を行った上で先頭のpathを取得する
      const fromPage = from.path.replace(/^\/m?\/?/, '').split('/')[0];
      const toPage = to.path.replace(/^\/m?\/?/, '').split('/')[0];
      // pages(画面)のルーティングが発生した場合の処理
      // '/path1/path1-1' から '/path1/path1-2' へ遷移するときはこの処理はしない
      if (fromPage !== toPage) {
        // キャッシュ情報を最新化する
        await this.synchronizeCache();
        // ユーザの設定情報を初期化する
        // this.initUserSetting({ employeeItemSettings: this.employeeItemSettings, authorities: this.loginUser.authorities });
        next();
      } else {
        next();
      }
    });
    // ページ遷移後の処理の登録
    this.$router.afterEach(to => {
      document.title = this.title;
      if (to.name !== PermittedRoute.Error) {
        // エラーページ以外へ遷移するときは、過去にエラー画面へ遷移したときのエラーキャッシュ情報をクリアする
        // (この処理を beforeEach で行うとエラー画面から別画面へ遷移するときに画面がちらつくので、afterEach で行う)
        this.clearError();
      }
    });

    const isRedirected = this.redirectToLogin();

    await Promise.all([this.findEnvironmentKbn(), this.synchronizeCache()]);

    // ログイン画面へ遷移しておらず、権限がない画面へアクセスした場合はエラー画面へ遷移する(エラーメッセージをサーバから取得するため、上記 synchronize の処理より後にする)
    if (!isRedirected && !this.checkRouteAuthority(this.currentRoute)) {
      this.$router.push({ name: PermittedRoute.Error });
    }

    customFilters.initFilter(() => this.message, () => this.multilingual);
    customMethods.initMethod(() => this.multilingual);
    veeValidate.initDictionary(() => this.message);

    // 初期表示のタイトルを設定する
    document.title = this.title;
    //          // ユーザの設定情報を初期化する
    //          this.initUserSetting({ employeeItemSettings: this.employeeItemSettings, authorities: this.loginUser.authorities });
  }
}