summaryrefslogtreecommitdiff
path: root/src/aggietime.js
blob: 413188aa2fd60ee9326ea995e0916c43203327b4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import {
  AGGIETIME_URI,
  AGGIETIME_DOMAIN,
  USER_PATH,
  USER_CACHE_EXP_SEC,
  CLOCKIN_PATH,
  CLOCKOUT_PATH,
  OPEN_SHIFT_PATH,
  OPEN_SHIFT_EXP_SEC,
} from "./constants.js";

import { with_exponential_retry } from "./exponential_retry.js";
import { client } from "./axios_client.js";

import expireCache from "expire-cache";

const aggietime = client.create({
  baseURL: AGGIETIME_URI,
});

const replace_path_args = (path, map) =>
  path.replaceAll(/:([a-zA-Z0-9_]+)/g, (_, key) => map[key]);

const get_user_position_or_specified = async (position_id) => {
  const { positions } = await get_user_info();

  if (position_id === undefined && positions.length != 1) {
    throw "Must specify a position when there's not only one to choose from";
  } else if (position_id === undefined) {
    return positions[0];
  }

  return position_id;
};

export const get_user_info = async () => {
  if (!expireCache.get("user")) {
    const user = await with_exponential_retry(() =>
      aggietime.get(USER_PATH).then(({ data, config }) => {
        const csrf_token = config.jar
          .toJSON()
          .cookies.find(
            ({ domain, key }) =>
              domain === AGGIETIME_DOMAIN && key === "XSRF-TOKEN"
          ).value;
        expireCache.set("aggietime-csrf", csrf_token);
        return data;
      })
    );

    expireCache.set("user", user, USER_CACHE_EXP_SEC);
  }
  return expireCache.get("user");
};

const do_clock_mutation = async (path, { position_id } = {}) => {
  position_id = await get_user_position_or_specified(position_id);

  return await aggietime
    .post(
      replace_path_args(path, { position_id }),
      {
        comment: "",
      },
      {
        headers: {
          "X-XSRF-TOKEN": expireCache.get("aggietime-csrf"),
        },
      }
    )
    .then(({ data }) => {
      expireCache.remove("status_line");
      return data;
    });
};

// Actions

export const clock_in = async (rest) => do_clock_mutation(CLOCKIN_PATH, rest);
export const clock_out = async (rest) => do_clock_mutation(CLOCKOUT_PATH, rest);

export const current_shift = async () => {
  const req_path = replace_path_args(OPEN_SHIFT_PATH, await get_user_info());
  const {
    request: {
      res: { responseUrl: response_url },
    },
    data,
  } = await aggietime.get(req_path);

  if (`${AGGIETIME_URI}/${req_path}` != response_url) {
    // IMO a very weird decision - when there is no open shift the api redirects
    // home instead of sending back a 404 or something actually *reasonable* :3
    return null;
  }

  return data;
};

export const get_status_line = async () => {
  if (!expireCache.get("status_line")) {
    const { anumber } = await get_user_info();
    const shift = await current_shift();
    let status_line = "No Shift";

    if (shift && shift?.start) {
      const start = new Date(shift?.start);
      status_line =
        ((new Date().getTime() - start.getTime()) / (1000 * 60 * 60)).toFixed(
          2
        ) + " hours";
    }

    expireCache.set(
      "status_line",
      `${anumber} - ${status_line}`,
      OPEN_SHIFT_EXP_SEC
    );
  }
  return { status: expireCache.get("status_line") };
};