Project import generated by Copybara.

GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/static_src/reducers/permissions.js b/static_src/reducers/permissions.js
new file mode 100644
index 0000000..2f0101b
--- /dev/null
+++ b/static_src/reducers/permissions.js
@@ -0,0 +1,118 @@
+// Copyright 2020 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+/**
+ * @fileoverview Permissions actions, selectors, and reducers organized into
+ * a single Redux "Duck" that manages updating and retrieving permissions state
+ * on the frontend.
+ *
+ * The Permissions data is stored in a normalized format.
+ * `permissions` stores all PermissionSets[] indexed by resource name.
+ *
+ * Reference: https://github.com/erikras/ducks-modular-redux
+ */
+
+import {combineReducers} from 'redux';
+import {createReducer, createRequestReducer} from './redux-helpers.js';
+
+import {prpcClient} from 'prpc-client-instance.js';
+
+import 'shared/typedef.js';
+
+/** @typedef {import('redux').AnyAction} AnyAction */
+
+// Permissions
+
+// Field Permissions
+export const FIELD_DEF_EDIT = 'FIELD_DEF_EDIT';
+export const FIELD_DEF_VALUE_EDIT = 'FIELD_DEF_VALUE_EDIT';
+
+// Actions
+export const BATCH_GET_START = 'permissions/BATCH_GET_START';
+export const BATCH_GET_SUCCESS = 'permissions/BATCH_GET_SUCCESS';
+export const BATCH_GET_FAILURE = 'permissions/BATCH_GET_FAILURE';
+
+/* State Shape
+{
+  byName: Object<string, PermissionSet>,
+
+  requests: {
+    batchGet: ReduxRequestState,
+  },
+}
+*/
+
+// Reducers
+
+/**
+ * All PermissionSets indexed by resource name.
+ * @param {Object<string, PermissionSet>} state The existing items.
+ * @param {AnyAction} action
+ * @param {Array<PermissionSet>} action.permissionSets
+ * @return {Object<string, PermissionSet>}
+ */
+export const byNameReducer = createReducer({}, {
+  [BATCH_GET_SUCCESS]: (state, {permissionSets}) => {
+    const newState = {...state};
+    for (const permissionSet of permissionSets) {
+      newState[permissionSet.resource] = permissionSet;
+    }
+    return newState;
+  },
+});
+
+const requestsReducer = combineReducers({
+  batchGet: createRequestReducer(
+      BATCH_GET_START, BATCH_GET_SUCCESS, BATCH_GET_FAILURE),
+});
+
+export const reducer = combineReducers({
+  byName: byNameReducer,
+
+  requests: requestsReducer,
+});
+
+// Selectors
+
+/**
+ * Returns all the PermissionSets in the store as a mapping.
+ * @param {any} state
+ * @return {Object<string, PermissionSet>}
+ */
+export const byName = (state) => state.permissions.byName;
+
+/**
+ * Returns the Permissions requests.
+ * @param {any} state
+ * @return {Object<string, ReduxRequestState>}
+ */
+export const requests = (state) => state.permissions.requests;
+
+// Action Creators
+
+/**
+ * Action creator to fetch PermissionSets.
+ * @param {Array<string>} names The resource names to get.
+ * @return {function(function): Promise<Array<PermissionSet>>}
+ */
+export const batchGet = (names) => async (dispatch) => {
+  dispatch({type: BATCH_GET_START});
+
+  try {
+    /** @type {{permissionSets: Array<PermissionSet>}} */
+    const {permissionSets} = await prpcClient.call(
+        'monorail.v3.Permissions', 'BatchGetPermissionSets', {names});
+
+    for (const permissionSet of permissionSets) {
+      if (!permissionSet.permissions) {
+        permissionSet.permissions = [];
+      }
+    }
+    dispatch({type: BATCH_GET_SUCCESS, permissionSets});
+
+    return permissionSets;
+  } catch (error) {
+    dispatch({type: BATCH_GET_FAILURE, error});
+  };
+};