Skip to content

Commit

Permalink
Support multiple APIs in one app #10514
Browse files Browse the repository at this point in the history
  • Loading branch information
anatol-sialitski committed Apr 23, 2024
1 parent e8cc38d commit 5fe9f78
Show file tree
Hide file tree
Showing 12 changed files with 214 additions and 83 deletions.
Original file line number Diff line number Diff line change
@@ -1,27 +1,25 @@
package com.enonic.xp.portal.impl.api;
package com.enonic.xp.api;

import com.enonic.xp.app.ApplicationKey;
import com.enonic.xp.page.DescriptorKey;
import com.enonic.xp.resource.ResourceKey;
import com.enonic.xp.security.PrincipalKeys;
import com.enonic.xp.security.RoleKeys;

public final class ApiDescriptor
{
private static final String API_DESCRIPTOR_PATH = "api/api.xml";

private final ApplicationKey applicationKey;
private final DescriptorKey key;

private final PrincipalKeys allowedPrincipals;

private ApiDescriptor( final Builder builder )
{
this.applicationKey = builder.applicationKey;
this.key = builder.key;
this.allowedPrincipals = builder.allowedPrincipals;
}

public ApplicationKey getApplicationKey()
public DescriptorKey key()
{
return applicationKey;
return key;
}

public PrincipalKeys getAllowedPrincipals()
Expand All @@ -35,9 +33,21 @@ public boolean isAccessAllowed( final PrincipalKeys principalKeys )
allowedPrincipals.stream().anyMatch( principalKeys::contains );
}

public static ResourceKey toResourceKey( final ApplicationKey applicationKey )
public ResourceKey toResourceKey( final String extension )
{
return ResourceKey.from( applicationKey, API_DESCRIPTOR_PATH );
return toResourceKey( this.key, extension );
}

public static ResourceKey toResourceKey( final DescriptorKey key, final String extension )
{
if ( "api".equals( key.getName() ) )
{
return ResourceKey.from( key.getApplicationKey(), "apis/api." + extension );
}
else
{
return ResourceKey.from( key.getApplicationKey(), "apis/" + key.getName() + "/" + key.getName() + "." + extension );
}
}

public static Builder create()
Expand All @@ -47,18 +57,17 @@ public static Builder create()

public static class Builder
{

private ApplicationKey applicationKey;
private DescriptorKey key;

private PrincipalKeys allowedPrincipals;

private Builder()
{
}

public Builder applicationKey( final ApplicationKey applicationKey )
public Builder key( final DescriptorKey key )
{
this.applicationKey = applicationKey;
this.key = key;
return this;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.enonic.xp.api;

import com.enonic.xp.annotation.PublicApi;
import com.enonic.xp.app.ApplicationKey;
import com.enonic.xp.page.DescriptorKey;

@PublicApi
public interface ApiDescriptorService
{
ApiDescriptor getByKey( DescriptorKey descriptorKey );

ApiDescriptors getByApplication( ApplicationKey applicationKey );
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.enonic.xp.api;

import com.google.common.collect.ImmutableList;

import com.enonic.xp.support.AbstractImmutableEntityList;

public final class ApiDescriptors
extends AbstractImmutableEntityList<ApiDescriptor>
{
private ApiDescriptors( final ImmutableList<ApiDescriptor> list )
{
super( list );
}

public static ApiDescriptors empty()
{
return new ApiDescriptors( ImmutableList.of() );
}

public static ApiDescriptors from( final ApiDescriptor... descriptors )
{
return from( ImmutableList.copyOf( descriptors ) );
}

public static ApiDescriptors from( final Iterable<ApiDescriptor> descriptors )
{
return new ApiDescriptors( ImmutableList.copyOf( descriptors ) );
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.enonic.xp.portal.impl.api;

import java.util.Set;

import com.google.common.collect.ImmutableSet;

import com.enonic.xp.app.ApplicationKey;
import com.enonic.xp.page.DescriptorKey;
import com.enonic.xp.resource.ResourceService;

public class ApiDescriptorKeyLocator
{
private final ResourceService service;

private final String pattern;

public ApiDescriptorKeyLocator( final ResourceService service )
{
this.service = service;
this.pattern = "^/apis/(api|(?<name>[^/]+)/\\k<name>)\\.(xml|js)$";
}

public Set<DescriptorKey> findKeys( final ApplicationKey key )
{
return this.service.findFiles( key, this.pattern )
.stream()
.map( resource -> DescriptorKey.from( key, getNameWithoutExtension( resource.getName() ) ) )
.collect( ImmutableSet.toImmutableSet() );
}

private static String getNameWithoutExtension( final String name )
{
final int pos = name.lastIndexOf( '.' );
return pos > 0 ? name.substring( 0, pos ) : name;
}
}

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,10 +1,19 @@
package com.enonic.xp.portal.impl.api;

import java.util.ArrayList;
import java.util.List;

import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.enonic.xp.api.ApiDescriptor;
import com.enonic.xp.api.ApiDescriptorService;
import com.enonic.xp.api.ApiDescriptors;
import com.enonic.xp.app.ApplicationKey;
import com.enonic.xp.page.DescriptorKey;
import com.enonic.xp.resource.Resource;
import com.enonic.xp.resource.ResourceProcessor;
import com.enonic.xp.resource.ResourceService;
Expand All @@ -14,34 +23,62 @@
public final class ApiDescriptorServiceImpl
implements ApiDescriptorService
{
private static final Logger LOG = LoggerFactory.getLogger( ApiDescriptorServiceImpl.class );

private final ResourceService resourceService;

private final ApiDescriptorKeyLocator descriptorKeyLocator;

@Activate
public ApiDescriptorServiceImpl( final @Reference ResourceService resourceService )
{
this.resourceService = resourceService;
this.descriptorKeyLocator = new ApiDescriptorKeyLocator( this.resourceService );
}

@Override
public ApiDescriptor getByKey( final DescriptorKey descriptorKey )
{
final ResourceProcessor<DescriptorKey, ApiDescriptor> processor = newRootProcessor( descriptorKey );
return resourceService.processResource( processor );
}

@Override
public ApiDescriptor getByApplication( final ApplicationKey applicationKey )
public ApiDescriptors getByApplication( final ApplicationKey applicationKey )
{
final ResourceProcessor<ApplicationKey, ApiDescriptor> processor = newRootProcessor( applicationKey );
return this.resourceService.processResource( processor );
final List<ApiDescriptor> list = new ArrayList<>();
for ( final DescriptorKey descriptorKey : descriptorKeyLocator.findKeys( applicationKey ) )
{
try
{
final ApiDescriptor descriptor = getByKey( descriptorKey );
if ( descriptor != null )
{
list.add( descriptor );
}
}
catch ( final IllegalArgumentException e )
{
LOG.error( "Error in api descriptor: {}", descriptorKey.toString(), e );
}
}

return ApiDescriptors.from( list );
}

private ResourceProcessor<ApplicationKey, ApiDescriptor> newRootProcessor( final ApplicationKey applicationKey )
private ResourceProcessor<DescriptorKey, ApiDescriptor> newRootProcessor( final DescriptorKey key )
{
return new ResourceProcessor.Builder<ApplicationKey, ApiDescriptor>().key( applicationKey )
return new ResourceProcessor.Builder<DescriptorKey, ApiDescriptor>().key( key )
.segment( "rootApiDescriptor" )
.keyTranslator( ApiDescriptor::toResourceKey )
.processor( resource -> loadDescriptor( applicationKey, resource ) )
.keyTranslator( descriptorKey -> ApiDescriptor.toResourceKey( descriptorKey, "xml" ) )
.processor( resource -> loadDescriptor( key, resource ) )
.build();
}

private ApiDescriptor loadDescriptor( final ApplicationKey key, final Resource resource )
private ApiDescriptor loadDescriptor( final DescriptorKey key, final Resource resource )
{
final ApiDescriptor.Builder builder = ApiDescriptor.create();
builder.applicationKey( key );
builder.key( key );
parseXml( resource, builder );
return builder.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import java.util.List;
import java.util.stream.Collectors;

import com.enonic.xp.api.ApiDescriptor;
import com.enonic.xp.security.PrincipalKey;
import com.enonic.xp.security.PrincipalKeys;
import com.enonic.xp.xml.DomElement;
Expand Down

0 comments on commit 5fe9f78

Please sign in to comment.