xref: /openbmc/webui-vue/src/store/api.js (revision c15672993166fd670ed832eb8da83c36537530c4)
1import Axios from 'axios';
2import router from '../router';
3import { setupCache, buildWebStorage } from 'axios-cache-interceptor';
4
5//Do not change store import.
6//Exact match alias set to support
7//dotenv customizations.
8import store from '.';
9
10Axios.defaults.headers.common['Accept'] = 'application/json';
11Axios.defaults.headers.common['X-Requested-With'] = 'XMLHttpRequest';
12
13const axiosInstance = Axios.create({
14  withCredentials: true,
15});
16
17const api = setupCache(axiosInstance, {
18  debug: console.log,
19  methods: ['get'],
20  interpretHeader: false,
21  etag: true,
22  modifiedSince: false,
23  staleIfError: false,
24  ttl: 0,
25  storage: buildWebStorage(localStorage, 'webui-vue-cache:'),
26});
27
28api.interceptors.response.use(undefined, (error) => {
29  let response = error.response;
30
31  // TODO: Provide user with a notification and way to keep system active
32  if (response.status == 401) {
33    if (response.config.url != '/login') {
34      window.location = '/login';
35      // Commit logout to remove XSRF-TOKEN cookie
36      store.commit('authentication/logout');
37    }
38  }
39
40  // Check if action is unauthorized.
41  if (response.status == 403) {
42    if (isPasswordExpired(response.data)) {
43      router.push('/change-password');
44    } else {
45      // Toast error message will appear on screen.
46      store.commit('global/setUnauthorized');
47    }
48  }
49
50  return Promise.reject(error);
51});
52
53export default {
54  get(path, config) {
55    return api.get(path, config);
56  },
57  delete(path, config) {
58    return api.delete(path, config);
59  },
60  post(path, payload, config) {
61    return api.post(path, payload, config);
62  },
63  patch(path, payload, config) {
64    return api.patch(path, payload, config);
65  },
66  put(path, payload, config) {
67    return api.put(path, payload, config);
68  },
69  all(promises) {
70    return Axios.all(promises);
71  },
72  spread(callback) {
73    return Axios.spread(callback);
74  },
75  set_auth_token(token) {
76    axiosInstance.defaults.headers.common['X-Auth-Token'] = token;
77  },
78};
79
80export const getResponseCount = (responses) => {
81  let successCount = 0;
82  let errorCount = 0;
83
84  responses.forEach((response) => {
85    if (response instanceof Error) errorCount++;
86    else successCount++;
87  });
88
89  return {
90    successCount,
91    errorCount,
92  };
93};
94
95export const isPasswordExpired = (data) => {
96  return !!findMessageId(data, 'PasswordChangeRequired');
97};
98
99/**
100 * Returns the first ExtendedInfo.Message to start with the
101 * Registry Name (Default: "Base") and end with the given key
102 * Ignore versions (.<X>.<Y>) --or-- (.<X>.<Y>.<Z>.),
103 *   but adhere to Registry namespace
104 * @param {object} data - AxiosResponse.data
105 * @param { {MessageKey: string}} key - key into the message registry
106 * @param { {MessageRegistryPrefix: string}} [registry=Base] - the name of the
107 *        message registry, undefined param defaults to "Base"
108 * @returns {ExtendedInfo.Message} ExtendedInfo.Message | undefined
109 */
110export const findMessageId = (data, key, registry = 'Base') => {
111  let extInfoMsgs = data?.['@Message.ExtendedInfo'];
112
113  return (
114    extInfoMsgs &&
115    extInfoMsgs.find((i) => {
116      const words = i.MessageId.split('.');
117      return words[words.length - 1] === key && words[0] === registry;
118    })
119  );
120};
121