Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,27 +33,22 @@ function EndpointActionsCard( {
description={ () => {
if ( endpoint.disabled && endpoint.disabled_error ) {
return `${ __(
'Endpoint disabled due to error:',
'Endpoint disabled due to error',
'newspack-plugin'
) }: ${ endpoint.disabled_error }`;
Comment on lines -36 to 38
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was printing two :, removed the one from the localized string

}
return (
<Fragment>
{ __( 'Actions:', 'newspack-plugin' ) }{ ' ' }
{ endpoint.global ? (
<span className="newspack-webhooks__endpoint-action">
{ __( 'global', 'newspack-plugin' ) }
{ endpoint.actions.map( action => (
<span
key={ action }
className="newspack-webhooks__endpoint-action"
>
{ action }
</span>
) : (
endpoint.actions.map( action => (
<span
key={ action }
className="newspack-webhooks__endpoint-action"
>
{ action }
</span>
) )
) }
) )
}
</Fragment>
);
} }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ const defaultEndpoint: Endpoint = {
disabled_error: false,
id: 0,
system: '',
global: true,
actions: [],
bearer_token: '',
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { useState, Fragment } from '@wordpress/element';
import { useState, useEffect, useRef, Fragment } from '@wordpress/element';
import {
CheckboxControl as WpCheckboxControl,
TextControl,
Expand All @@ -24,6 +24,7 @@ import {
Modal,
Grid,
} from '../../../../../../../components/src';
import { validateEndpoint, validateUrl } from '../utils';

/**
* Checkbox control props override.
Expand Down Expand Up @@ -55,12 +56,20 @@ const Upsert = ( {
message?: string;
} >( {} );

const modalRef = useRef( null as HTMLElement | null );

const onSuccess = ( endpointId: string | number, response: Endpoint[] ) => {
setEndpoints( response );
setAction( null, endpointId );
};

function upsertEndpoint( endpointToUpsert: Endpoint ) {
const errors = validateEndpoint( endpointToUpsert );
if ( errors.length ) {
setError( errors.join( ' ' ) );
return;
}
setError( null );
wizardApiFetch< Endpoint[] >(
{
path: `/newspack/v1/webhooks/endpoints/${
Expand All @@ -78,9 +87,14 @@ const Upsert = ( {
}

function testEndpoint(
url: string | undefined,
url: string,
Comment on lines -81 to +90
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not sure why this would ever be undefined so I removed it

bearer_token: string | undefined
) {
const urlError = validateUrl( url );
if ( urlError ) {
setError( urlError );
return;
}
wizardApiFetch< { success: boolean; code: number; message: string } >(
{
path: '/newspack/v1/webhooks/endpoints/test',
Expand Down Expand Up @@ -111,9 +125,16 @@ const Upsert = ( {
);
}

useEffect( () => {
if ( errorMessage ) {
modalRef?.current?.querySelector('.components-modal__content')?.scrollTo( { top: 0, left: 0, behavior: 'smooth' } );
}
}, [ errorMessage ] );

return (
<Fragment>
<Modal
ref={ modalRef }
title={ __( 'Webhook Endpoint', 'newspack-plugin' ) }
onRequestClose={ () => {
setAction( null, endpoint.id );
Expand Down Expand Up @@ -204,31 +225,19 @@ const Upsert = ( {
/>
<Grid columns={ 1 } gutter={ 16 }>
<h3>{ __( 'Actions', 'newspack-plugin' ) }</h3>
<CheckboxControl
checked={ editing.global }
onChange={ ( value: boolean ) =>
setEditing( { ...editing, global: value } )
}
label={ __( 'Global', 'newspack-plugin' ) }
help={ __(
'Leave this checked if you want this endpoint to receive data from all actions.',
'newspack-plugin'
) }
disabled={ inFlight }
/>
{ actions.length > 0 && (
<Fragment>
<p>
{ __(
'If this endpoint is not global, select which actions should trigger this endpoint:',
'Select which actions should trigger this endpoint:',
'newspack-plugin'
) }
</p>
<Grid columns={ 2 } gutter={ 16 }>
{ actions.map( ( actionKey, i ) => (
<CheckboxControl
key={ i }
disabled={ editing.global || inFlight }
disabled={ inFlight }
label={ actionKey }
checked={
( editing.actions &&
Expand All @@ -237,7 +246,6 @@ const Upsert = ( {
) ) ||
false
}
indeterminate={ editing.global }
onChange={ () => {
const currentActions =
editing.actions || [];
Expand Down
40 changes: 40 additions & 0 deletions src/wizards/newspack/views/settings/connections/webhooks/utils.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
/**
* WordPress dependencies
*/
import { __ } from '@wordpress/i18n';
import { Fragment } from '@wordpress/element';
import { settings, check, close, reusableBlock } from '@wordpress/icons';

Expand Down Expand Up @@ -81,3 +82,42 @@ export function getRequestStatusIcon(
export function hasEndpointErrors( endpoint: Endpoint ): boolean {
return endpoint.requests.some( request => request.errors.length );
}

/**
* Validate endpoint URL.
*
* @param url The URL to validate.
* @return Error message if URL is invalid, false otherwise.
*/
export function validateUrl( url: string ): string | false {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be modernized to use URL interface for example:

export function validateUrl( url: string ): string | false {
	if ( ! url ) {
		return __( 'URL is required.', 'newspack-plugin' );
	}
	try {
		const urlObject = new URL( url );
		// Ensure the protocol is either http or https
		if ( ! [ 'http:', 'https:' ].includes( urlObject.protocol ) ) {
			return __( 'URL must use HTTP or HTTPS protocol.', 'newspack-plugin' );
		}
		return false;
	} catch ( error ) {
		return __( 'Invalid URL format.', 'newspack-plugin' );
	}
}

There is also a function inside @wordpress/url called isUrl which also validates urls using the above pattern.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call! Updated in 9eb2589. While at it, I also restricted to HTTPS, which is enforced in the backend.

if ( ! url ) {
return __( 'URL is required.', 'newspack-plugin' );
}
try {
const urlObject = new URL( url );
if ( urlObject.protocol !== 'https:' ) {
return __( 'HTTPS protocol is required for the endpoint URL.', 'newspack-plugin' );
}
return false;
} catch ( error ) {
return __( 'Invalid URL format.', 'newspack-plugin' );
}
}

/**
* Validate an endpoint.
*
* @param endpoint The endpoint to validate.
* @return An array of error messages.
*/
export function validateEndpoint( endpoint: Endpoint ): string[] {
const errors = [];
const urlError = validateUrl( endpoint.url );
if ( urlError ) {
errors.push( urlError );
}
if ( ! endpoint.actions || ! endpoint.actions.length ) {
errors.push( __( 'At least one action is required.', 'newspack-plugin' ) );
}
return errors;
}
1 change: 0 additions & 1 deletion src/wizards/newspack/views/settings/types.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,6 @@ type Endpoint = {
disabled_error: boolean;
id: string | number;
system: string;
global: boolean;
actions: string[];
bearer_token?: string;
};
Expand Down