Skip to content

Commit

Permalink
Support sortOrder for SSO connections (#855)
Browse files Browse the repository at this point in the history
* [Create SSO] Add sortOrder input, pass in payload only if non empty

* Negative values not accepted

* Display hint text for sortOrder

* sortOrder field in Edit view

* [Connection List] Sort if `displaySorted` is set

* Remove sorting logic in UI, rely on API sorting

* Use nullish coalescing instead of OR
  • Loading branch information
niwsa authored Feb 6, 2024
1 parent 53198b1 commit f238870
Show file tree
Hide file tree
Showing 6 changed files with 107 additions and 17 deletions.
3 changes: 3 additions & 0 deletions src/sso/connections/ConnectionList/index.lite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ export default function ConnectionList(props: ConnectionListProps) {
if (props.product) {
urlParams.set('product', props.product);
}
if (props.displaySorted) {
urlParams.set('sort', 'true');
}
if (urlParams.toString()) {
return `${urlPath}?${urlParams}`;
}
Expand Down
30 changes: 27 additions & 3 deletions src/sso/connections/CreateConnection/oidc/index.lite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const INITIAL_VALUES = {
'oidcMetadata.token_endpoint': '',
'oidcMetadata.jwks_uri': '',
'oidcMetadata.userinfo_endpoint': '',
sortOrder: '' as unknown as string | number,
},
};

Expand All @@ -43,7 +44,7 @@ export default function CreateOIDCConnection(props: CreateConnectionProps) {
const state = useStore({
oidcConnection: INITIAL_VALUES.oidcConnection,
isSaving: false,
updateConnection(data: Partial<typeof INITIAL_VALUES.oidcConnection>) {
updateConnection(data: Partial<OIDCSSOConnection>) {
return { ...state.oidcConnection, ...data };
},
handleChange(event: Event) {
Expand All @@ -62,8 +63,11 @@ export default function CreateOIDCConnection(props: CreateConnectionProps) {
formObj.oidcMetadata = {} as Exclude<OIDCSSOConnection['oidcMetadata'], undefined>;
}
formObj.oidcMetadata[key.replace('oidcMetadata.', '')] = val;
} else if (key === 'sortOrder') {
// pass sortOrder only if set to non-empty string
val !== '' && (formObj[key] = +val); // convert sortOrder into number
} else {
formObj[key as keyof Omit<OIDCSSOConnection, 'oidcMetadata'>] = val;
formObj[key as keyof Omit<OIDCSSOConnection, 'oidcMetadata'>] = val as string;
}
});
state.isSaving = true;
Expand Down Expand Up @@ -140,7 +144,7 @@ export default function CreateOIDCConnection(props: CreateConnectionProps) {

onUpdate(() => {
if (props.defaults) {
// Remove SAML only setting
// forceAuthn is a SAML only setting, remove it
const { forceAuthn, tenant, ...rest } = props.defaults;
const _tenant = Array.isArray(tenant) ? tenant[0] : tenant;
state.oidcConnection = state.updateConnection({ ...rest, tenant: _tenant });
Expand Down Expand Up @@ -380,6 +384,26 @@ export default function CreateOIDCConnection(props: CreateConnectionProps) {
autocomplete='one-time-code'
/>
<Spacer y={6} />
<Show when={state.formVariant === 'advanced'}>
<Show when={!state.isExcluded('sortOrder')}>
<InputField
label='Sort Order'
id='sortOrder'
classNames={state.classes.inputField}
type='number'
min='0'
placeholder='10'
readOnly={state.isReadOnly('sortOrder')}
value={state.oidcConnection.sortOrder as string}
handleInputChange={state.handleChange}
/>
<div id='sortOrder-hint' class={defaultClasses.hint}>
Connections will be sorted (in a listing view like IdP Selection) using this setting. Higher
values will be displayed first.
</div>
</Show>
</Show>
<Spacer y={6} />
{/* TODO: bring loading state */}
{/* TODO: bring translation support */}
<div class={defaultClasses.formAction}>
Expand Down
26 changes: 25 additions & 1 deletion src/sso/connections/CreateConnection/saml/index.lite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ const INITIAL_VALUES = {
defaultRedirectUrl: '',
rawMetadata: '',
metadataUrl: '',
sortOrder: '' as unknown as string | number,
forceAuthn: false as boolean,
},
};
Expand All @@ -53,9 +54,12 @@ export default function CreateSAMLConnection(props: CreateConnectionProps) {
save(event: Event) {
event.preventDefault();
state.isSaving = true;
const { sortOrder, ...rest } = state.samlConnection;
// pass sortOrder only if set to non-empty string
const payload = sortOrder === '' ? rest : { ...state.samlConnection, sortOrder: +sortOrder };
saveConnection<SAMLSSORecord>({
url: props.urls.post,
formObj: state.samlConnection,
formObj: payload,
connectionIsSAML: true,
callback: async (data) => {
state.isSaving = false;
Expand Down Expand Up @@ -298,6 +302,26 @@ export default function CreateSAMLConnection(props: CreateConnectionProps) {
handleInputChange={state.handleChange}
/>
<Spacer y={6} />
<Show when={state.formVariant === 'advanced'}>
<Show when={!state.isExcluded('sortOrder')}>
<InputField
label='Sort Order'
id='sortOrder'
classNames={state.classes.inputField}
type='number'
min='0'
placeholder='10'
readOnly={state.isReadOnly('sortOrder')}
value={state.samlConnection.sortOrder as string}
handleInputChange={state.handleChange}
/>
<div id='sortOrder-hint' class={defaultClasses.hint}>
Connections will be sorted (in a listing view like IdP Selection) using this setting. Higher
values will be displayed first.
</div>
</Show>
</Show>
<Spacer y={6} />
<Show when={state.formVariant === 'advanced'}>
<Show when={!state.isExcluded('forceAuthn')}>
<Checkbox
Expand Down
22 changes: 22 additions & 0 deletions src/sso/connections/EditConnection/oidc/index.lite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,9 @@ export default function EditOIDCConnection(props: EditOIDCConnectionProps) {
formObj.oidcMetadata = {} as OIDCSSOConnection['oidcMetadata'];
}
formObj.oidcMetadata![key.replace('oidcMetadata.', '')] = val;
} else if (key === 'sortOrder') {
// pass sortOrder only if set to non-empty string
val !== '' && (formObj[key] = +val); // convert sortOrder into number
} else {
formObj[key as keyof Omit<OIDCSSOConnection, 'oidcMetadata'>] = val;
}
Expand Down Expand Up @@ -183,6 +186,7 @@ export default function EditOIDCConnection(props: EditOIDCConnectionProps) {
'oidcMetadata.token_endpoint': _connection.oidcProvider.metadata?.token_endpoint || '',
'oidcMetadata.jwks_uri': _connection.oidcProvider.metadata?.jwks_uri || '',
'oidcMetadata.userinfo_endpoint': _connection.oidcProvider.metadata?.userinfo_endpoint || '',
sortOrder: _connection.sortOrder ?? '',
};
}
state.hasDiscoveryUrl = _connection.oidcProvider.discoveryUrl ? true : false;
Expand Down Expand Up @@ -383,6 +387,24 @@ export default function EditOIDCConnection(props: EditOIDCConnectionProps) {
placeholder='https://example.com/userinfo'
/>
<Spacer y={6} />
<Show when={state.formVariant === 'advanced'}>
<Show when={!state.isExcluded('sortOrder')}>
<InputField
label='Sort Order'
id='sortOrder'
classNames={state.classes.inputField}
type='number'
min='0'
placeholder='10'
value={state.oidcConnection.sortOrder as string}
handleInputChange={state.handleChange}
/>
<div id='sortOrder-hint' class={defaultClasses.hint}>
Connections will be sorted (in a listing view like IdP Selection) using this setting. Higher
values will be displayed first.
</div>
</Show>
</Show>
<div class={defaultClasses.formAction}>
<Show when={typeof props.cancelCallback === 'function'}>
<Button type='button' name='Cancel' handleClick={props.cancelCallback} variant='outline' />
Expand Down
34 changes: 23 additions & 11 deletions src/sso/connections/EditConnection/saml/index.lite.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ const INITIAL_VALUES = {
rawMetadata: '',
metadataUrl: '',
forceAuthn: false as boolean,
sortOrder: '' as unknown as string | number,
} as SAMLFormState,
};

Expand Down Expand Up @@ -85,17 +86,9 @@ export default function EditSAMLConnection(props: EditSAMLConnectionProps) {
},
saveSSOConnection(event: Event) {
event.preventDefault();
const payload =
props.variant === 'advanced'
? { ...state.samlConnection }
: {
tenant: state.samlConnection.tenant,
product: state.samlConnection.product,
clientID: state.samlConnection.clientID,
clientSecret: state.samlConnection.clientSecret,
rawMetadata: state.samlConnection.rawMetadata,
metadataUrl: state.samlConnection.metadataUrl,
};
const { sortOrder, ...rest } = state.samlConnection;
// pass sortOrder only if set to non-empty string
const payload = sortOrder === '' ? rest : { ...state.samlConnection, sortOrder: +sortOrder! };
state.isSaving = true;
saveConnection<undefined>({
url: props.urls.patch,
Expand Down Expand Up @@ -172,6 +165,7 @@ export default function EditSAMLConnection(props: EditSAMLConnectionProps) {
rawMetadata: _connection.rawMetadata || '',
metadataUrl: _connection.metadataUrl || '',
forceAuthn: _connection.forceAuthn === true,
sortOrder: _connection.sortOrder ?? '',
};
}
}
Expand Down Expand Up @@ -320,6 +314,24 @@ export default function EditSAMLConnection(props: EditSAMLConnectionProps) {
</Show>
</Show>
<Spacer y={6} />
<Show when={state.formVariant === 'advanced'}>
<Show when={!state.isExcluded('sortOrder')}>
<InputField
label='Sort Order'
id='sortOrder'
classNames={state.classes.inputField}
type='number'
min='0'
placeholder='10'
value={state.samlConnection.sortOrder as string}
handleInputChange={state.handleChange}
/>
<div id='sortOrder-hint' class={defaultClasses.hint}>
Connections will be sorted (in a listing view like IdP Selection) using this setting. Higher
values will be displayed first.
</div>
</Show>
</Show>
<div class={defaultClasses.formAction}>
<Show when={typeof props.cancelCallback === 'function'}>
<Button type='button' name='Cancel' handleClick={props.cancelCallback} variant='outline' />
Expand Down
9 changes: 7 additions & 2 deletions src/sso/connections/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ export interface ConnectionListProps {
tableProps?: TableProps;
tenant?: string | string[];
product?: string;
// If true will sort the list display based on sortOrder of the connection
displaySorted?: boolean;
}

export interface CreateConnectionProps {
Expand Down Expand Up @@ -55,7 +57,8 @@ export interface CreateConnectionProps {
/** Use this boolean to toggle the header display on/off. Useful when using the connection component standalone */
displayHeader?: boolean;
defaults?: Partial<
Omit<SSOConnection, 'tenant'> & Pick<SAMLSSOConnection, 'forceAuthn'> & { tenant: string[] | string }
Omit<SSOConnection, 'tenant' | 'sortOrder'> &
Pick<SAMLSSOConnection, 'forceAuthn'> & { tenant: string[] | string }
>;
}

Expand All @@ -76,7 +79,7 @@ export interface CreateSSOConnectionProps
defaults?: ConnectionsWrapperProp['defaults'];
}

type FormObjValues = string | boolean | string[] | undefined;
type FormObjValues = string | boolean | string[] | undefined | number;

export type FormObj = Record<string, FormObjValues | Record<string, any>>;

Expand Down Expand Up @@ -117,6 +120,8 @@ interface SSOConnection {
name?: string;
label?: string;
description?: string;
// to support null values use empty string ''
sortOrder?: string | number;
}

export interface SAMLSSOConnection extends SSOConnection {
Expand Down

0 comments on commit f238870

Please sign in to comment.