Skip to content

Commit

Permalink
Add custom fields route and use for custom fields mixin
Browse files Browse the repository at this point in the history
  • Loading branch information
Nitzperetz committed Dec 1, 2024
1 parent c0c835e commit 4a291bc
Show file tree
Hide file tree
Showing 11 changed files with 100 additions and 7 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
export const apiPaths = {
user: {
me: '/v1/auth/me',
customAttributes: '/v1/mgmt/user/customattributes',
},
};
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { apiPaths } from '../apiPaths';
import { HttpClient } from '../types';
import { CustomAttr, HttpClient } from '../types';
import { withErrorHandler } from './helpers';
import { user } from './mocks';

Expand All @@ -21,7 +21,21 @@ export const createUserSdk = ({
return res.json();
};

const getCustomAttributes = async (): Promise<CustomAttr[]> => {
if (mock) {
return [];
}
const res = await httpClient.get(apiPaths.user.customAttributes);

await withErrorHandler(res);

const json = await res.json();

return json.data;
};

return {
me,
getCustomAttributes,
};
};
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import '@descope/core-js-sdk';
import createWebSdk from '@descope/web-js-sdk';
import { createUserSdk } from './createUserSdk';
import '@descope/core-js-sdk';

declare const BUILD_VERSION: string;

Expand Down
13 changes: 11 additions & 2 deletions packages/widgets/user-profile-widget/src/lib/widget/api/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export type Sdk = ReturnType<typeof createWebSdk>;

type CustomAttributeType = string | boolean | number;

type CustomAttributes = Record<string, CustomAttributeType>;
export type CustomAttributes = Record<string, CustomAttributeType>;

type UserStatus = 'enabled' | 'disabled' | 'invited';

Expand Down Expand Up @@ -94,11 +94,20 @@ export type CreateUserConfig = {

export type CustomAttr = {
name: string;
type: number;
type: AttributeType;
options: string[];
displayName: string;
defaultValue: Record<string, string>;
ViewPermissions: string[];
EditPermissions: string[];
editable: boolean;
};

export enum AttributeType {
text = 1,
number = 2,
boolean = 3,
singleSelect = 4,
array = 5,
date = 6,
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ export const initWidgetRootMixin = createSingletonMixin(
async init() {
await super.init?.();

await Promise.all([this.actions.getMe(), this.#initWidgetRoot()]);
await Promise.all([
this.actions.getMe(),
this.actions.getCustomAttributes(),
this.#initWidgetRoot(),
]);

this.onWidgetRootReady();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
initLifecycleMixin,
loggerMixin,
} from '@descope/sdk-mixins';
import { getMe, logout } from '../state/asyncActions';
import { getCustomAttributes, getMe, logout } from '../state/asyncActions';
import { initialState } from '../state/initialState';
import { apiMixin } from './apiMixin';

Expand All @@ -19,10 +19,12 @@ export const stateManagementMixin = createSingletonMixin(
extraReducers: (builder) => {
getMe.reducer(builder);
logout.reducer(builder);
getCustomAttributes.reducer(builder);
},
asyncActions: {
getMe: getMe.action,
logout: logout.action,
getCustomAttributes: getCustomAttributes.action,
},
}),
initLifecycleMixin,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/* eslint-disable no-param-reassign */
/* eslint-disable @typescript-eslint/no-shadow */
import { createAsyncThunk } from '@reduxjs/toolkit';
import { Sdk } from '../../api/sdk';
import { FirstParameter, State, ThunkConfigExtraApi } from '../types';
import { buildAsyncReducer, withRequestStatus } from './helpers';

const action = createAsyncThunk<
any,
FirstParameter<Sdk['user']['getCustomAttributes']>,
ThunkConfigExtraApi
>('customAttributes', (arg, { extra: { api } }) =>
api.user.getCustomAttributes(),
);

const reducer = buildAsyncReducer(action)(
{
onFulfilled: (state, action) => {
state.customAttributes.data = action.payload;
},
},
withRequestStatus((state: State) => state.customAttributes),
);

export const getCustomAttributes = { action, reducer };
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
export * from './getMe';
export * from './logout';
export * from './getCustomAttributes';
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,9 @@ export const initialState: State = {
error: null,
data: {},
},
customAttributes: {
loading: false,
error: null,
data: [],
},
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { createSelector } from 'reselect';
import { AttributeType } from '../api/types';
import { State } from './types';

export const getMe = (state: State) => state.me.data;
Expand All @@ -18,7 +19,32 @@ export const getIsPhoneVerified = createSelector(
export const getHasPasskey = createSelector(getMe, (me) => me.webauthn);
export const getHasPassword = createSelector(getMe, (me) => me.password);

export const getCustomAttributes = (state: State) =>
state.customAttributes.data;

export const getUserCustomAttrs = createSelector(
getMe,
(me) => me.customAttributes,
getCustomAttributes,
(userData, allCustomAttrs = []) => {
const res: Record<string, string> = {};
const userCustomAttributes = userData['customAttributes'] || {};

Object.keys(userCustomAttributes).forEach((key: string) => {
const type =
allCustomAttrs.find((attr) => attr.name === key)?.type ||
AttributeType.text;
if (type === AttributeType.date && userCustomAttributes[key]) {
// to full date time
res[key] = new Date(userCustomAttributes[key]).toLocaleString();
} else if (
type === AttributeType.boolean &&
userCustomAttributes[key] !== undefined
) {
res[key] = !userCustomAttributes[key] ? 'False' : 'True';
} else {
res[key] = (userCustomAttributes[key] || '').toString();
}
});
return res;
},
);
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
import { ActionReducerMapBuilder } from '@reduxjs/toolkit';
import { Sdk } from '../api/sdk';
import { CustomAttr } from '../api/types';

export type State = {
me: {
loading: boolean;
error: unknown;
data: Record<string, any>;
};
customAttributes: {
loading: boolean;
error: unknown;
data: CustomAttr[];
};
};

type First<T extends any[]> = T extends [infer U, ...any[]] ? U : never;
Expand Down

0 comments on commit 4a291bc

Please sign in to comment.