Skip to content

Commit

Permalink
apiUrl function #10628
Browse files Browse the repository at this point in the history
  • Loading branch information
anatol-sialitski committed Sep 18, 2024
1 parent 1dc67bd commit 8500e3a
Show file tree
Hide file tree
Showing 12 changed files with 607 additions and 65 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.enonic.xp.lib.portal.url;

import java.util.Set;

import com.google.common.collect.Multimap;

import com.enonic.xp.portal.url.ApiUrlParams;

public final class ApiUrlHandler
extends AbstractUrlHandler
{
private static final Set<String> VALID_URL_PROPERTY_KEYS = Set.of( "application", "api", "type", "params" );

@Override
protected String buildUrl( final Multimap<String, String> map )
{
final ApiUrlParams params = new ApiUrlParams().portalRequest( this.request ).setAsMap( map );
return this.urlService.apiUrl( params );
}

@Override
protected boolean isValidParam( final String param )
{
return VALID_URL_PROPERTY_KEYS.contains( param );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const portalLib = require('/lib/xp/portal');
const assert = require('/lib/xp/testing');

// BEGIN
const url = portalLib.apiUrl({
application: 'com.enonic.app.myapp',
api: 'myapi',
params: {
a: 1,
b: 2
}
});

const unnamedApiUrl = portalLib.apiUrl({
application: 'com.enonic.app.myapp',
});

// END

assert.assertEquals('ApiUrlParams{type=server, params={a=[1], b=[2]}, api=myapi, application=com.enonic.app.myapp}', url);
assert.assertEquals('ApiUrlParams{type=server, params={}, application=com.enonic.app.myapp}', unnamedApiUrl);
29 changes: 29 additions & 0 deletions modules/lib/lib-portal/src/main/resources/lib/xp/portal.ts
Original file line number Diff line number Diff line change
Expand Up @@ -600,3 +600,32 @@ export function imagePlaceholder(params: ImagePlaceholderParams): string {
bean.setHeight(params?.height ?? 0);
return bean.createImagePlaceholder();
}

export interface ApiUrlParams {
application: string;
api?: string;
type?: 'server' | 'absolute' | 'websocket';
params?: object;
}

interface ApiUrlHandler {
createUrl(value: object): string;
}

/**
* This function generates a URL pointing to a Universal API.
*
* @example-ref examples/portal/apiUrl.js
*
* @param {object} params Input parameters as JSON.
* @param {string} params.application Application to reference to the API.
* @param {string} [params.api] Name of the API
* @param {string} [params.type=server] URL type. Either `server` (server-relative URL) or `absolute` or `websocket`.
* @param {object} [params.params] Custom parameters to append to the URL.
*
* @returns {string} The generated URL.
*/
export function apiUrl(params: ApiUrlParams): string {
const bean = __.newBean<ApiUrlHandler>('com.enonic.xp.lib.portal.url.ApiUrlHandler');
return bean.createUrl(__.toScriptValue(params));
}
Original file line number Diff line number Diff line change
Expand Up @@ -196,4 +196,10 @@ public void testExample_imagePlaceholder()
{
runScript( "/lib/xp/examples/portal/imagePlaceholder.js" );
}

@Test
public void testExample_apiUrl()
{
runScript( "/lib/xp/examples/portal/apiUrl.js" );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.enonic.xp.portal.url;

import java.util.Objects;

import com.google.common.base.MoreObjects;
import com.google.common.base.Strings;
import com.google.common.collect.Multimap;

import com.enonic.xp.annotation.PublicApi;

@PublicApi
public final class ApiUrlParams
extends AbstractUrlParams<ApiUrlParams>
{
private String application;

private String api;

public String getApplication()
{
return application;
}

public String getApi()
{
return api;
}

public ApiUrlParams application( final String value )
{
this.application = Objects.requireNonNull( value );
return this;
}

public ApiUrlParams api( final String value )
{
this.api = Strings.emptyToNull( value );
return this;
}

@Override
public ApiUrlParams setAsMap( final Multimap<String, String> map )
{
super.setAsMap( map );
api( singleValue( map, "_api" ) );
application( singleValue( map, "_application" ) );
getParams().putAll( map );
return this;
}

@Override
protected void buildToString( final MoreObjects.ToStringHelper helper )
{
super.buildToString( helper );
helper.add( "api", this.api );
helper.add( "application", this.application );
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,6 @@ public interface PortalUrlService
String generateUrl( GenerateUrlParams params );

String processHtml( ProcessHtmlParams params );

String apiUrl( ApiUrlParams params );
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,18 @@

import java.math.BigInteger;
import java.util.Collection;
import java.util.Iterator;
import java.util.Map;
import java.util.Objects;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Splitter;
import com.google.common.collect.LinkedListMultimap;
import com.google.common.collect.Multimap;
import com.google.common.net.UrlEscapers;

import com.enonic.xp.content.ContentService;
import com.enonic.xp.exception.NotFoundException;
Expand All @@ -31,8 +26,6 @@
import com.enonic.xp.web.servlet.ServletRequestUrlHelper;
import com.enonic.xp.web.servlet.UriRewritingResult;

import static com.google.common.base.Strings.isNullOrEmpty;

abstract class PortalUrlBuilder<T extends AbstractUrlParams>
{
private static final Logger LOG = LoggerFactory.getLogger( PortalUrlBuilder.class );
Expand All @@ -55,72 +48,17 @@ public final void setParams( final T params )

protected final void appendPart( final StringBuilder str, final String urlPart )
{
if ( isNullOrEmpty( urlPart ) )
{
return;
}

final boolean endsWithSlash = ( str.length() > 0 ) && ( str.charAt( str.length() - 1 ) == '/' );
final String normalized = normalizePath( urlPart );

if ( !endsWithSlash )
{
str.append( "/" );
}

str.append( normalized );
UrlBuilderHelper.appendPart( str, urlPart );
}

private void appendParams( final StringBuilder str, final Collection<Map.Entry<String, String>> params )
{
if ( params.isEmpty() )
{
return;
}
str.append( "?" );
final Iterator<Map.Entry<String, String>> it = params.iterator();
appendParam( str, it.next() );
while ( it.hasNext() )
{
str.append( "&" );
appendParam( str, it.next() );
}
}

private void appendParam( final StringBuilder str, final Map.Entry<String, String> param )
{
final String value = param.getValue();
str.append( urlEncode( param.getKey() ) );
if ( value != null )
{
str.append( "=" ).append( urlEncode( value ) );
}
}

private String urlEncode( final String value )
{
return UrlEscapers.urlFormParameterEscaper().escape( value );
}

private String urlEncodePathSegment( final String value )
{
return UrlEscapers.urlPathSegmentEscaper().escape( value );
UrlBuilderHelper.appendParams( str, params );
}

String normalizePath( final String value )
{
if ( value == null )
{
return null;
}

if ( !value.contains( "/" ) )
{
return urlEncodePathSegment( value );
}

return StreamSupport.stream( Splitter.on( '/' ).trimResults().omitEmptyStrings().split( value ).spliterator(), false ).
map( this::urlEncodePathSegment ).collect( Collectors.joining( "/" ) );
return UrlBuilderHelper.normalizePath( value );
}

public final String build()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
import com.enonic.xp.portal.impl.PortalConfig;
import com.enonic.xp.portal.impl.RedirectChecksumService;
import com.enonic.xp.portal.url.AbstractUrlParams;
import com.enonic.xp.portal.url.ApiUrlParams;
import com.enonic.xp.portal.url.AssetUrlParams;
import com.enonic.xp.portal.url.AttachmentUrlParams;
import com.enonic.xp.portal.url.ComponentUrlParams;
Expand Down Expand Up @@ -139,6 +140,25 @@ public String processHtml( final ProcessHtmlParams params )
return new RichTextProcessor( styleDescriptorService, this, macroService ).process( params );
}

@Override
public String apiUrl( final ApiUrlParams params )
{
if ( params == null || params.getApplication() == null )
{
throw new IllegalArgumentException( "\"application\" is required" );
}

if ( params.getPortalRequest() != null )
{
return build( new UniversalApiUrlBuilder(), params );
}
else
{
// TODO resolve baseUrl
return new SlashApiUrlBuilder( params ).build();
}
}

private <B extends PortalUrlBuilder<P>, P extends AbstractUrlParams> String build( final B builder, final P params )
{
builder.setParams( params );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.enonic.xp.portal.impl.url;

import com.enonic.xp.portal.url.ApiUrlParams;

import static com.enonic.xp.portal.impl.url.UrlBuilderHelper.appendParams;
import static com.enonic.xp.portal.impl.url.UrlBuilderHelper.appendPart;

public class SlashApiUrlBuilder
{
private final ApiUrlParams params;

public SlashApiUrlBuilder( ApiUrlParams params )
{
this.params = params;
}

public String build()
{
final StringBuilder url = new StringBuilder();
appendPart( url, "api" );
appendPart( url, params.getApplication() );
if ( params.getApi() != null )
{
appendPart( url, params.getApi() );
}
appendParams( url, params.getParams().entries() );
return url.toString();
}
}
Loading

0 comments on commit 8500e3a

Please sign in to comment.