Skip to content

Commit

Permalink
add UpstreamFeedNotYetAvailableException for V3 and internal controller
Browse files Browse the repository at this point in the history
  • Loading branch information
hbruch committed Apr 26, 2024
1 parent 3d84bee commit 1b3a164
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,12 +127,6 @@ public Object getInternalGbfsFeedForProvider(
} catch (IllegalArgumentException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
} catch (NoSuchElementException e) {
// if system_id is well known and feed is a required one, it should exist and
// is not available due to upstream issues.
// In this case, we should respond with 5xx and not 4xx
if (feedProviderService.getFeedProviderBySystemId(systemId) != null) {
throw new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE);
}
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
}
}
Expand Down Expand Up @@ -205,8 +199,38 @@ protected Object getFeed(String systemId, String feed) {
var data = feedCache.find(feedName, feedProvider);

if (data == null) {
throwsIfFeedCouldOrShouldExist(feedName, feedProvider);
throw new NoSuchElementException();
}
return data;
}

/*
Throws an UpstreamFeedNotYetAvailableException, if either the discoveryFile (gbf file) is not yet cached,
the requested feed is published in the discovery file, or the discovery file is malformed.
*/
protected void throwsIfFeedCouldOrShouldExist(
GBFSFeedName feedName,
FeedProvider feedProvider
) {
try {
GBFS discoveryFile = (GBFS) feedCache.find(GBFSFeedName.GBFS, feedProvider);
if (
discoveryFile == null ||
((GBFS) discoveryFile).getFeedsData()
.values()
.stream()
.map(GBFSFeeds::getFeeds)
.flatMap(list -> list.stream())
.map(GBFSFeed::getName)
.anyMatch(name -> name.equals(feedName))
) {
throw new UpstreamFeedNotYetAvailableException();
}
} catch (NullPointerException e) {
// in case the gbfs is malformed, e.g. no languages are defined, or no feeds,
// this is an upstream error and the requested feed might exist
throw new UpstreamFeedNotYetAvailableException();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ protected Object getFeed(String systemId, String feed) {
}

/*
Returns true, if either the discoveryFile (gbf file) is not yet cached,
Throws an UpstreamFeedNotYetAvailableException, if either the discoveryFile (gbf file) is not yet cached,
the requested feed is published in the discovery file, or the discovery file is malformed.
*/
protected void throwsIfFeedCouldOrShouldExist(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
import java.util.NoSuchElementException;
import java.util.concurrent.TimeUnit;
import org.entur.gbfs.v3_0.gbfs.GBFSFeed;
import org.entur.gbfs.v3_0.gbfs.GBFSGbfs;
import org.entur.gbfs.v3_0.manifest.GBFSManifest;
import org.entur.lamassu.cache.GBFSV3FeedCache;
import org.entur.lamassu.model.provider.FeedProvider;
import org.entur.lamassu.service.FeedProviderService;
import org.entur.lamassu.service.SystemDiscoveryService;
import org.entur.lamassu.util.CacheUtil;
Expand Down Expand Up @@ -105,12 +107,6 @@ public ResponseEntity<Object> getV3Feed(
} catch (IllegalArgumentException e) {
throw new ResponseStatusException(HttpStatus.BAD_REQUEST);
} catch (NoSuchElementException e) {
// if system_id is well known and feed is a required one, it should exist and
// is not available due to upstream issues.
// In this case, we should respond with 5xx and not 4xx
if (feedProviderService.getFeedProviderBySystemId(systemId) != null) {
throw new ResponseStatusException(HttpStatus.BAD_GATEWAY);
}
throw new ResponseStatusException(HttpStatus.NOT_FOUND);
}
}
Expand All @@ -127,8 +123,40 @@ protected Object getFeed(String systemId, String feed) {
var data = v3FeedCache.find(feedName, feedProvider);

if (data == null) {
throwsIfFeedCouldOrShouldExist(feedName, feedProvider);
throw new NoSuchElementException();
}
return data;
}

/*
Throws an UpstreamFeedNotYetAvailableException, if either the discoveryFile (gbf file) is not yet cached,
the requested feed is published in the discovery file, or the discovery file is malformed.
*/
protected void throwsIfFeedCouldOrShouldExist(
GBFSFeed.Name feedName,
FeedProvider feedProvider
) {
try {
GBFSGbfs discoveryFile = (GBFSGbfs) v3FeedCache.find(
GBFSFeed.Name.GBFS,
feedProvider
);
if (
discoveryFile == null ||
discoveryFile
.getData()
.getFeeds()
.stream()
.map(GBFSFeed::getName)
.anyMatch(name -> name.equals(feedName))
) {
throw new UpstreamFeedNotYetAvailableException();
}
} catch (NullPointerException e) {
// in case the gbfs is malformed, e.g. no languages are defined, or no feeds,
// this is an upstream error and the requested feed might exist
throw new UpstreamFeedNotYetAvailableException();
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package org.entur.lamassu.controller;

import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

import java.util.List;
import java.util.Map;
import org.entur.gbfs.v3_0.gbfs.GBFSData;
import org.entur.gbfs.v3_0.gbfs.GBFSFeed;
import org.entur.gbfs.v3_0.gbfs.GBFSGbfs;
import org.entur.lamassu.cache.GBFSV3FeedCache;
import org.entur.lamassu.model.provider.FeedProvider;
import org.entur.lamassu.service.FeedProviderService;
import org.entur.lamassu.service.SystemDiscoveryService;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.springframework.web.server.ResponseStatusException;

public class GBFSV3FeedControllerTest {

public static final String KNOWN_SYSTEM_ID = "knownSystem";
private GBFSV3FeedController feedController;
private FeedProviderService mockedFeedProviderService;

@Rule
public ExpectedException expectedException = ExpectedException.none();

private GBFSV3FeedCache mockedFeedCache;

@Before
public void before() {
SystemDiscoveryService systemDiscoveryService = mock(SystemDiscoveryService.class);
mockedFeedCache = mock(GBFSV3FeedCache.class);
mockedFeedProviderService = mock(FeedProviderService.class);

feedController =
new GBFSV3FeedController(
systemDiscoveryService,
mockedFeedCache,
mockedFeedProviderService
);
}

@Test
public void throws400OnNonGBFSFeedRequest() {
expectedException.expect(ResponseStatusException.class);
expectedException.expectMessage("400 BAD_REQUEST");

feedController.getV3Feed("anySystem", "no-gbfs-feed");
}

@Test
public void throws404OnNonConfiguredSystemRequest() {
expectedException.expect(ResponseStatusException.class);
expectedException.expectMessage("404 NOT_FOUND");
feedController.getV3Feed("unknownSystem", "gbfs");
}

@Test
public void throws502OnConfiguredSystemButUnavailableFeedRequest() {
FeedProvider feedProvider = new FeedProvider();
feedProvider.setSystemId(KNOWN_SYSTEM_ID);
when(mockedFeedProviderService.getFeedProviderBySystemId(KNOWN_SYSTEM_ID))
.thenReturn(feedProvider);

expectedException.expect(UpstreamFeedNotYetAvailableException.class);
feedController.getV3Feed(KNOWN_SYSTEM_ID, "gbfs");
}

@Test
public void throws404OnConfiguredSystemButUndeclaredFeedRequest() {
var feedProvider = new FeedProvider();
feedProvider.setSystemId(KNOWN_SYSTEM_ID);
var gbfs = createDiscoveryFileWithFeed(GBFSFeed.Name.GBFS);

when(mockedFeedProviderService.getFeedProviderBySystemId(KNOWN_SYSTEM_ID))
.thenReturn(feedProvider);
when(mockedFeedCache.find(GBFSFeed.Name.GBFS, feedProvider)).thenReturn(gbfs);
when(mockedFeedCache.find(GBFSFeed.Name.GEOFENCING_ZONES, feedProvider))
.thenReturn(null);
expectedException.expect(ResponseStatusException.class);
expectedException.expectMessage("404 NOT_FOUND");
feedController.getV3Feed(KNOWN_SYSTEM_ID, "geofencing_zones");
}

@Test
public void throws502OnConfiguredSystemAndDeclaredFeedRequest() {
var feedProvider = new FeedProvider();
feedProvider.setSystemId(KNOWN_SYSTEM_ID);
var gbfs = createDiscoveryFileWithFeed(GBFSFeed.Name.GEOFENCING_ZONES);

when(mockedFeedProviderService.getFeedProviderBySystemId(KNOWN_SYSTEM_ID))
.thenReturn(feedProvider);
when(mockedFeedCache.find(GBFSFeed.Name.GBFS, feedProvider)).thenReturn(gbfs);
when(mockedFeedCache.find(GBFSFeed.Name.GEOFENCING_ZONES, feedProvider))
.thenReturn(null);
expectedException.expect(UpstreamFeedNotYetAvailableException.class);
feedController.getV3Feed(KNOWN_SYSTEM_ID, "geofencing_zones");
}

@Test
public void throws502OnConfiguredSystemAndMalformedDiscoveryFeedRequest() {
var feedProvider = new FeedProvider();
feedProvider.setSystemId(KNOWN_SYSTEM_ID);
// GBFS is malformed, as it has no feeds defined
var gbfs = new GBFSGbfs();

when(mockedFeedProviderService.getFeedProviderBySystemId(KNOWN_SYSTEM_ID))
.thenReturn(feedProvider);
when(mockedFeedCache.find(GBFSFeed.Name.GBFS, feedProvider)).thenReturn(gbfs);
when(mockedFeedCache.find(GBFSFeed.Name.GEOFENCING_ZONES, feedProvider))
.thenReturn(null);
expectedException.expect(UpstreamFeedNotYetAvailableException.class);
feedController.getV3Feed(KNOWN_SYSTEM_ID, "geofencing_zones");
}

public GBFSGbfs createDiscoveryFileWithFeed(GBFSFeed.Name feedName) {
var gbfs = new GBFSGbfs();
var data = new GBFSData();
var feed = new GBFSFeed().withName(feedName);
data.setFeeds(List.of(feed));
gbfs.setData(data);
return gbfs;
}
}

0 comments on commit 1b3a164

Please sign in to comment.