mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-15 06:49:56 +00:00
Merge branch 'master' into translation
This commit is contained in:
commit
e7b7a95dda
34 files changed
+859
-85
No files matched your search
+5
-2
@@ -1,14 +1,17 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
public class BranchIOConstants {
|
||||
public static final String SAILINSIGHT_2_APP_BRANCHIO = "https://sailinsight20-app.sapsailing.com/invite";
|
||||
public static final String SAILINSIGHT_APP_BRANCHIO = "https://sailinsight-app.sapsailing.com/invite";
|
||||
public static final String SAILINSIGHT_2_APP_BRANCHIO = "https://sailinsight20-app.sapsailing.com/invite";
|
||||
public static final String SAILINSIGHT_3_APP_BRANCHIO = "https://sailinsight30-app.sapsailing.com/invite";
|
||||
public static final String SAILINSIGHT_APP_BRANCHIO_PATH = "checkinUrl";
|
||||
public static final String BUOYPINGER_APP_BRANCHIO = "https://buoypinger-app.sapsailing.com/invite";
|
||||
public static final String BUOYPINGER_APP_BRANCHIO_PATH = "checkinUrl";
|
||||
public static final String OPEN_REGATTA_2_APP_BRANCHIO = "https://sailinsight20-app.sapsailing.com/publicInvite";
|
||||
public static final String OPEN_REGATTA_2_APP_BRANCHIO_PATH = "checkinUrl";
|
||||
public static final String OPEN_REGATTA_3_APP_BRANCHIO = "https://sailinsight30-app.sapsailing.com/publicInvite";
|
||||
public static final String OPEN_REGATTA_3_APP_BRANCHIO_PATH = "checkinUrl";
|
||||
|
||||
private BranchIOConstants() {
|
||||
};
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -3,7 +3,8 @@ package com.sap.sailing.domain.common;
|
||||
public enum MailInvitationType {
|
||||
LEGACY,
|
||||
SailInsight1,
|
||||
SailInsight2;
|
||||
SailInsight2,
|
||||
SailInsight3;
|
||||
|
||||
public static final String SYSTEM_PROPERTY_NAME = "com.sap.sailing.domain.tracking.MailInvitationType";
|
||||
}
|
||||
+5
@@ -1159,6 +1159,11 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
UUID id = (UUID) eventDBObject.get(FieldNames.EVENT_ID.name());
|
||||
TimePoint startDate = loadTimePoint(eventDBObject, FieldNames.EVENT_START_DATE);
|
||||
TimePoint endDate = loadTimePoint(eventDBObject, FieldNames.EVENT_END_DATE);
|
||||
if (endDate.before(startDate)) {
|
||||
logger.warning("End date "+endDate+" of event "+name+" with ID "+id+" is before its start date "+
|
||||
startDate+"; adjusting such that end date equals start date.");
|
||||
endDate = startDate;
|
||||
}
|
||||
boolean isPublic = eventDBObject.get(FieldNames.EVENT_IS_PUBLIC.name()) != null
|
||||
? (Boolean) eventDBObject.get(FieldNames.EVENT_IS_PUBLIC.name()) : false;
|
||||
Venue venue = loadVenue((Document) eventDBObject.get(FieldNames.VENUE.name()));
|
||||
|
||||
-2
@@ -5,7 +5,6 @@ import java.util.Locale;
|
||||
import com.sap.sailing.domain.common.BranchIOConstants;
|
||||
|
||||
public class BranchIO2RaceLogTrackingInvitationMailBuilder extends BranchIORaceLogTrackingInvitationMailBuilder {
|
||||
|
||||
BranchIO2RaceLogTrackingInvitationMailBuilder(Locale locale) {
|
||||
super(locale);
|
||||
}
|
||||
@@ -20,5 +19,4 @@ public class BranchIO2RaceLogTrackingInvitationMailBuilder extends BranchIORaceL
|
||||
// FIXME the app should in the future at some point also support bouy pinging, replace this then
|
||||
return BranchIOConstants.BUOYPINGER_APP_BRANCHIO;
|
||||
}
|
||||
|
||||
}
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
package com.sap.sailing.domain.racelogtracking.impl;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import com.sap.sailing.domain.common.BranchIOConstants;
|
||||
|
||||
public class BranchIO3RaceLogTrackingInvitationMailBuilder extends BranchIORaceLogTrackingInvitationMailBuilder {
|
||||
|
||||
BranchIO3RaceLogTrackingInvitationMailBuilder(Locale locale) {
|
||||
super(locale);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSailInsightBranchIO() {
|
||||
return BranchIOConstants.SAILINSIGHT_3_APP_BRANCHIO;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getBouyPingerBranchIO() {
|
||||
// FIXME the app should in the future at some point also support bouy pinging, replace this then
|
||||
return BranchIOConstants.BUOYPINGER_APP_BRANCHIO;
|
||||
}
|
||||
|
||||
}
|
||||
+3
@@ -362,6 +362,9 @@ public class RaceLogTrackingAdapterImpl implements RaceLogTrackingAdapter {
|
||||
case SailInsight2:
|
||||
mail = new BranchIO2RaceLogTrackingInvitationMailBuilder(locale);
|
||||
break;
|
||||
case SailInsight3:
|
||||
mail = new BranchIO3RaceLogTrackingInvitationMailBuilder(locale);
|
||||
break;
|
||||
default:
|
||||
throw new IllegalArgumentException("Unhandled mail type");
|
||||
}
|
||||
|
||||
+15
-1
@@ -7,6 +7,7 @@ import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Base64;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
@@ -330,7 +331,7 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
|
||||
builder = child.getTextContent();
|
||||
break;
|
||||
case "dxtDate":
|
||||
issueDate = new MillisecondsTimePoint(isoTimestampFormat.parse(child.getTextContent()+"+0000")); // assume UTC
|
||||
issueDate = new MillisecondsTimePoint(parseDate(child.getTextContent())); // assume UTC
|
||||
break;
|
||||
case "CertType":
|
||||
certType = Integer.valueOf(child.getTextContent().trim());
|
||||
@@ -350,6 +351,19 @@ public class ORCPublicCertificateDatabaseImpl implements ORCPublicCertificateDat
|
||||
sailNumber, boatClassName, designer, builder, yearBuilt, issueDate, certType, isOneDesign, isProvisional);
|
||||
}
|
||||
|
||||
private Date parseDate(String dateString) throws ParseException {
|
||||
StringBuilder stringBuilder = new StringBuilder(dateString);
|
||||
char timeZoneCharacter = stringBuilder.charAt(stringBuilder.length()-1);
|
||||
if(timeZoneCharacter== 'z' || timeZoneCharacter == 'Z'|| timeZoneCharacter == 'X') {
|
||||
stringBuilder.deleteCharAt(stringBuilder.length()-1);
|
||||
}
|
||||
if(false == stringBuilder.toString().contains("+")) {
|
||||
stringBuilder.append("+0000");
|
||||
}
|
||||
|
||||
return isoTimestampFormat.parse(stringBuilder.toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ORCCertificate getCertificate(String referenceNumber) throws Exception {
|
||||
final HttpClient client = new SystemDefaultHttpClient();
|
||||
|
||||
+1
@@ -166,6 +166,7 @@ public class QRCodePlace extends AbstractBasePlace {
|
||||
}
|
||||
|
||||
public String getPublicInviteBranchIOUrl() {
|
||||
// TODO why is this specific to version 2 and not considering the default server setting for mailInvitationType?
|
||||
return BranchIOConstants.OPEN_REGATTA_2_APP_BRANCHIO + "?" + QRCodePlace.PARAM_REGATTA_NAME + "="
|
||||
+ encodeUrl(publicRegattaName) + "&" + QRCodePlace.PARAM_REGATTA_SECRET + "="
|
||||
+ encodeUrl(regattaRegistrationLinkSecret) + "&" + QRCodePlace.PARAM_SERVER + "="
|
||||
|
||||
-3
@@ -144,12 +144,9 @@ public class RegattaDetailsComposite extends Composite {
|
||||
Button button = new Button(stringMessages.registrationLinkShare(), new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
|
||||
RegistrationLinkWithQRCode registrationLinkWithQRCode = new RegistrationLinkWithQRCode();
|
||||
registrationLinkWithQRCode.setSecret(regatta.registrationLinkSecret);
|
||||
|
||||
sailingService.getMailType(new AsyncCallback<MailInvitationType>() {
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
logger.log(Level.SEVERE, "Could not determine MailInvitationType", caught);
|
||||
|
||||
+13
-6
@@ -221,21 +221,28 @@ public class RegattaLogAddDeviceMappingDialog extends DataEntryDialogWithDateTim
|
||||
String url = DeviceMappingConstants.getDeviceMappingForRegattaLogUrl(baseUrlWithoutTrailingSlash,
|
||||
eventIdAsString, leaderboardName, mappedItemType, mappedItemId, regattaRegisterSecret,
|
||||
GwtUrlHelper.INSTANCE);
|
||||
if (mailInvitationType == MailInvitationType.LEGACY) {
|
||||
switch (mailInvitationType) {
|
||||
case LEGACY:
|
||||
// URL is already legacy
|
||||
} else if (mailInvitationType == MailInvitationType.SailInsight1) {
|
||||
break;
|
||||
case SailInsight1:
|
||||
url = BranchIOConstants.SAILINSIGHT_APP_BRANCHIO + "?"
|
||||
+ BranchIOConstants.SAILINSIGHT_APP_BRANCHIO_PATH + "="
|
||||
+ GwtUrlHelper.INSTANCE.encodeQueryString(url);
|
||||
} else if (mailInvitationType == MailInvitationType.SailInsight2) {
|
||||
break;
|
||||
case SailInsight2:
|
||||
url = BranchIOConstants.SAILINSIGHT_2_APP_BRANCHIO + "?"
|
||||
+ BranchIOConstants.OPEN_REGATTA_2_APP_BRANCHIO_PATH + "="
|
||||
+ GwtUrlHelper.INSTANCE.encodeQueryString(url);
|
||||
}
|
||||
else {
|
||||
break;
|
||||
case SailInsight3:
|
||||
url = BranchIOConstants.SAILINSIGHT_3_APP_BRANCHIO + "?"
|
||||
+ BranchIOConstants.OPEN_REGATTA_3_APP_BRANCHIO_PATH + "="
|
||||
+ GwtUrlHelper.INSTANCE.encodeQueryString(url);
|
||||
break;
|
||||
default:
|
||||
throw new QRCodeURLCreationException("Unknown mail invitation type: " + mailInvitationType);
|
||||
}
|
||||
|
||||
return url;
|
||||
}
|
||||
});
|
||||
|
||||
+3
-16
@@ -63,14 +63,10 @@ public class RegistrationLinkWithQRCodeDialog extends DataEntryDialog<Registrati
|
||||
this.invitationType = invitationType;
|
||||
this.registrationLinkWithQRCode = registrationLinkWithQRCode == null ? new RegistrationLinkWithQRCode()
|
||||
: registrationLinkWithQRCode;
|
||||
|
||||
|
||||
registrationLinkPanel = new CaptionPanel(stringMessages.registrationLinkUrl());
|
||||
registrationLinkPanelContent = new VerticalPanel();
|
||||
registrationLinkPanel.add(registrationLinkPanelContent);
|
||||
|
||||
final Label eventExplain = new Label(stringMessages.event());
|
||||
|
||||
events = createGenericListBox(new ValueBuilder<EventDTO>() {
|
||||
@Override
|
||||
public String getValue(EventDTO item) {
|
||||
@@ -98,10 +94,8 @@ public class RegistrationLinkWithQRCodeDialog extends DataEntryDialog<Registrati
|
||||
updateDisplayWidgets();
|
||||
}
|
||||
});
|
||||
|
||||
registrationLinkPanelContent.add(eventExplain);
|
||||
registrationLinkPanelContent.add(events);
|
||||
|
||||
registrationLinkExplain = new Label(stringMessages.registrationLinkUrlExplain());
|
||||
registrationLinkPanelContent.add(registrationLinkExplain);
|
||||
urlTextBox = createTextBox("URL", 100);
|
||||
@@ -109,7 +103,6 @@ public class RegistrationLinkWithQRCodeDialog extends DataEntryDialog<Registrati
|
||||
registrationLinkPanelContent.add(urlTextBox);
|
||||
Anchor copyAnchor = new Anchor(stringMessages.registrationLinkUrlCopy());
|
||||
copyAnchor.addClickHandler(new ClickHandler() {
|
||||
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
urlTextBox.setFocus(true);
|
||||
@@ -118,7 +111,6 @@ public class RegistrationLinkWithQRCodeDialog extends DataEntryDialog<Registrati
|
||||
}
|
||||
});
|
||||
registrationLinkPanelContent.add(copyAnchor);
|
||||
|
||||
barcodePanel = new CaptionPanel(stringMessages.registrationLinkDialogQrcode());
|
||||
barcodePanelContent = new VerticalPanel();
|
||||
barcodePanel.add(barcodePanelContent);
|
||||
@@ -144,7 +136,6 @@ public class RegistrationLinkWithQRCodeDialog extends DataEntryDialog<Registrati
|
||||
}
|
||||
final VerticalPanel dialogPanel = new VerticalPanel();
|
||||
panel.add(dialogPanel);
|
||||
|
||||
dialogPanel.add(registrationLinkPanel);
|
||||
dialogPanel.add(barcodePanel);
|
||||
return panel;
|
||||
@@ -155,29 +146,27 @@ public class RegistrationLinkWithQRCodeDialog extends DataEntryDialog<Registrati
|
||||
Notification.notify(stringMessages.noEventSelected(), NotificationType.ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
String baseUrl = GWT.getHostPageBaseURL();
|
||||
if (baseUrl.endsWith("/")) {
|
||||
baseUrl = baseUrl.substring(0, baseUrl.lastIndexOf("/"));
|
||||
baseUrl = baseUrl.substring(0, baseUrl.indexOf("/gwt"));
|
||||
}
|
||||
|
||||
final String eventIdAsString = events.getValue().id.toString();
|
||||
|
||||
Map<String, String> parameters = new HashMap<String, String>();
|
||||
parameters.put("regatta_name", regattaName);
|
||||
parameters.put("secret", secret);
|
||||
parameters.put("server", baseUrl);
|
||||
parameters.put("event_id", eventIdAsString);
|
||||
// TODO decide OPEN_REGATTA link variant based on invitationType
|
||||
String deeplinkUrl = BranchIO.generateLink(BranchIOConstants.OPEN_REGATTA_2_APP_BRANCHIO, parameters,
|
||||
URL::encodeQueryString);
|
||||
urlTextBox.setText(deeplinkUrl);
|
||||
if (invitationType != MailInvitationType.SailInsight2) {
|
||||
if (invitationType != MailInvitationType.SailInsight2 &&
|
||||
invitationType != MailInvitationType.SailInsight3) { // TODO it would be better to make it a property of an enum literal whether open regatta invitation is possible
|
||||
getStatusLabel().setText(stringMessages.warningSailInsightVersion());
|
||||
getStatusLabel().setStyleName("errorLabel");
|
||||
}
|
||||
sailingService.openRegattaRegistrationQrCode(deeplinkUrl, new AsyncCallback<String>() {
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
GWT.log("Qrcode generation failed: ", caught);
|
||||
@@ -187,9 +176,7 @@ public class RegistrationLinkWithQRCodeDialog extends DataEntryDialog<Registrati
|
||||
public void onSuccess(String result) {
|
||||
GWT.log("Qrcode generated for url: " + deeplinkUrl);
|
||||
qrCodeImage.setUrl("data:image/png;base64, " + result);
|
||||
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+17
-10
@@ -192,7 +192,13 @@ public class TaggingPanel extends ComponentWithoutSettings
|
||||
|
||||
tagButtons = new ArrayList<TagButton>();
|
||||
|
||||
taggingPanel = new DockLayoutPanel(Style.Unit.PX);
|
||||
taggingPanel = new DockLayoutPanel(Style.Unit.PX) {
|
||||
@Override
|
||||
public void onResize() {
|
||||
super.onResize();
|
||||
taggingPanel.setWidgetSize(footerPanel, calculateFooterPanelHeight());
|
||||
}
|
||||
};
|
||||
filterbarAndContentPanel = new HeaderPanel();
|
||||
tagFooterPanel = new TagFooterPanel(this, sailingService, stringMessages, userService);
|
||||
footerPanel = new ScrollPanel(tagFooterPanel);
|
||||
@@ -705,15 +711,7 @@ public class TaggingPanel extends ComponentWithoutSettings
|
||||
Scheduler.get().scheduleFinally(new RepeatingCommand() {
|
||||
@Override
|
||||
public boolean execute() {
|
||||
// Default size of 2/3 the TaggingPanel
|
||||
int height = (int) Math.round(taggingPanel.getOffsetHeight() / 3f * 2f);
|
||||
// Get actual size of the footerPanel which might be 0 if the browser screwed up
|
||||
final int vScroll = footerPanel.getWidget().getElement().getScrollHeight();
|
||||
if (vScroll != 0) {
|
||||
height = Math.min(height, vScroll);
|
||||
}
|
||||
height = Math.max(200, height);
|
||||
taggingPanel.setWidgetSize(footerPanel, height);
|
||||
taggingPanel.setWidgetSize(footerPanel, calculateFooterPanelHeight());
|
||||
taggingPanel.animate(FOOTERPANEL_ANIMATION_PERIOD_MS);
|
||||
return false;
|
||||
}
|
||||
@@ -728,6 +726,15 @@ public class TaggingPanel extends ComponentWithoutSettings
|
||||
}
|
||||
}
|
||||
|
||||
private int calculateFooterPanelHeight() {
|
||||
// Start with footerPanel content height
|
||||
int height = footerPanel.getWidget().getElement().getOffsetHeight();
|
||||
// Limit size if we take up the entire height of the viewing area
|
||||
// to make sure we don't get pushed out of it at the top making parts of the footerPanel inaccessible
|
||||
height = Math.min(height, taggingPanel.getOffsetHeight());
|
||||
return height;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether current user has permission to modify public tags.
|
||||
*
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
-->
|
||||
<system-property>
|
||||
<name>webdriver.chrome.driver</name>
|
||||
<value>C:\apps\ChromeSeleniumDriver\chromedriver.exe</value>
|
||||
<value>/usr/bin/chromedriver</value>
|
||||
</system-property>
|
||||
<!--
|
||||
You need geckodriver to be able to run Selenium tests on Firefox.
|
||||
@@ -50,7 +50,7 @@
|
||||
-->
|
||||
<system-property>
|
||||
<name>webdriver.gecko.driver</name>
|
||||
<value>/path/to/geckodriver.exe</value>
|
||||
<value>/usr/bin/geckodriver</value>
|
||||
</system-property>
|
||||
<!--
|
||||
geckodriver automatically finds an installed version of Firefox.
|
||||
@@ -86,12 +86,14 @@
|
||||
The capabilities section can be used for fine tuning of the driver (e.g. version). For more informations see the
|
||||
pages mentioned above.
|
||||
-->
|
||||
<!--
|
||||
<!-- -->
|
||||
<driver-definition class="org.openqa.selenium.firefox.FirefoxDriver">
|
||||
</driver-definition>
|
||||
-->
|
||||
<!-- -->
|
||||
<!--
|
||||
<driver-definition class="org.openqa.selenium.chrome.ChromeDriver">
|
||||
</driver-definition>
|
||||
-->
|
||||
<!--
|
||||
<driver-definition class="org.openqa.selenium.ie.InternetExplorerDriver">
|
||||
<capabilities>
|
||||
|
||||
+114
-4
@@ -26,21 +26,60 @@ public class LeaderboardApi {
|
||||
private static final String SET_TRACKING_TIMES_URL = LEADERBOARDS_V1_RESOURCE_URL
|
||||
+ "/{leaderboardname}/settrackingtimes";
|
||||
private static final String START_TRACKING_URL = LEADERBOARDS_V1_RESOURCE_URL + "/{leaderboardname}/starttracking";
|
||||
private static final String UPDATE_LEADERBOARD_URL = LEADERBOARDS_V1_RESOURCE_URL + "/{leaderboardname}/update";
|
||||
|
||||
private static final String PARAM_RESULT_DISCARDING_THRESHOLDS = "resultDiscardingThresholds";
|
||||
private static final String PARAM_LEADERBOARD_DISPLAY_NAME = "leaderboardDisplayName";
|
||||
|
||||
public JSONArray getLeaderboards(ApiContext ctx) {
|
||||
return ctx.get(LEADERBOARDS_LIST_URL);
|
||||
}
|
||||
|
||||
public JSONObject getLeaderboard(ApiContext ctx, String leaderboardName) {
|
||||
return ctx.get(toUrl(LEADERBOARD_URL, leaderboardName));
|
||||
public Leaderboard getLeaderboard(ApiContext ctx, String leaderboardName) {
|
||||
return new Leaderboard(ctx.get(toUrl(LEADERBOARD_URL, leaderboardName)));
|
||||
}
|
||||
|
||||
public DeviceMappingRequest createDeviceMappingRequest(final ApiContext ctx, final String leaderboardName) {
|
||||
return new DeviceMappingRequest(ctx, leaderboardName);
|
||||
}
|
||||
|
||||
public TrackingTimes setTrackingTimes(ApiContext ctx, String leaderboardName, String raceColumnName, String fleetName,
|
||||
Long startTime, Long endTime) {
|
||||
public void updateLeaderboard(ApiContext ctx, String leaderboardName, String leaderboardDisplayName,
|
||||
int[] resultDiscardingThresholds) {
|
||||
JSONObject requestJson = new JSONObject();
|
||||
JSONArray resultDiscardingThresholdsJsonArray = new JSONArray();
|
||||
if (resultDiscardingThresholds != null) {
|
||||
for (int i = 0; i < resultDiscardingThresholds.length; i++) {
|
||||
resultDiscardingThresholdsJsonArray.add(resultDiscardingThresholds[i]);
|
||||
}
|
||||
}
|
||||
requestJson.put(PARAM_RESULT_DISCARDING_THRESHOLDS,
|
||||
resultDiscardingThresholds != null ? resultDiscardingThresholdsJsonArray : null);
|
||||
requestJson.put(PARAM_LEADERBOARD_DISPLAY_NAME, leaderboardDisplayName);
|
||||
ctx.post(toUrl(UPDATE_LEADERBOARD_URL, leaderboardName), null, requestJson);
|
||||
}
|
||||
|
||||
public void updateLeaderboardDisplayName(ApiContext ctx, String leaderboardName, String leaderboardDisplayName) {
|
||||
JSONObject requestJson = new JSONObject();
|
||||
requestJson.put(PARAM_LEADERBOARD_DISPLAY_NAME, leaderboardDisplayName);
|
||||
ctx.post(toUrl(UPDATE_LEADERBOARD_URL, leaderboardName), null, requestJson);
|
||||
}
|
||||
|
||||
public void updateLeaderboardResultDiscardingThresholds(ApiContext ctx, String leaderboardName,
|
||||
int[] resultDiscardingThresholds) {
|
||||
JSONObject requestJson = new JSONObject();
|
||||
JSONArray resultDiscardingThresholdsJsonArray = new JSONArray();
|
||||
if (resultDiscardingThresholds != null) {
|
||||
for (int i = 0; i < resultDiscardingThresholds.length; i++) {
|
||||
resultDiscardingThresholdsJsonArray.add(resultDiscardingThresholds[i]);
|
||||
}
|
||||
}
|
||||
requestJson.put(PARAM_RESULT_DISCARDING_THRESHOLDS,
|
||||
resultDiscardingThresholds != null ? resultDiscardingThresholdsJsonArray : null);
|
||||
ctx.post(toUrl(UPDATE_LEADERBOARD_URL, leaderboardName), null, requestJson);
|
||||
}
|
||||
|
||||
public TrackingTimes setTrackingTimes(ApiContext ctx, String leaderboardName, String raceColumnName,
|
||||
String fleetName, Long startTime, Long endTime) {
|
||||
final Map<String, String> queryParams = new TreeMap<>();
|
||||
queryParams.put("race_column", raceColumnName);
|
||||
queryParams.put("fleet", fleetName);
|
||||
@@ -63,6 +102,77 @@ public class LeaderboardApi {
|
||||
return urlTemplate.replace("{leaderboardname}", leaderboardName);
|
||||
}
|
||||
|
||||
public class Leaderboard extends JsonWrapper {
|
||||
|
||||
public Leaderboard(JSONObject json) {
|
||||
super(json);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return get("name");
|
||||
}
|
||||
|
||||
public String getDisplayName() {
|
||||
return get("displayName");
|
||||
}
|
||||
|
||||
public Long getResultTimePoint() {
|
||||
return get("resultTimepoint");
|
||||
}
|
||||
|
||||
public String getResultState() {
|
||||
return get("resultState");
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return get("type");
|
||||
}
|
||||
|
||||
public String getShardingLeaderboardName() {
|
||||
return get("shardingLeaderboardName");
|
||||
}
|
||||
|
||||
public int[] getDiscardIndexResultsStartingWithHowManyRaces() {
|
||||
final JSONArray resultDiscardingThresholdsRaw = (JSONArray) get(
|
||||
"discardIndexResultsStartingWithHowManyRaces");
|
||||
final int[] resultDiscardingThresholds = new int[resultDiscardingThresholdsRaw.size()];
|
||||
for (int i = 0; i < resultDiscardingThresholdsRaw.size(); i++) {
|
||||
resultDiscardingThresholds[i] = ((Long) resultDiscardingThresholdsRaw.get(i)).intValue();
|
||||
}
|
||||
return resultDiscardingThresholds;
|
||||
}
|
||||
|
||||
public Boolean canBoatsOfCompetitorsChangePerRace() {
|
||||
return get("canBoatsOfCompetitorsChangePerRace");
|
||||
}
|
||||
|
||||
public Long getMaxCompetitorsCount() {
|
||||
return get("maxCompetitorsCount");
|
||||
}
|
||||
|
||||
public String getScoringComment() {
|
||||
return get("scoringComment");
|
||||
}
|
||||
|
||||
public Long getLastScoringUpdate() {
|
||||
return get("lastScoringUpdate");
|
||||
}
|
||||
|
||||
public String[] getColumnNames() {
|
||||
final JSONArray columnNamesRaw = (JSONArray) get("columnNames");
|
||||
final String[] columnNames = new String[columnNamesRaw.size()];
|
||||
for (int i = 0; i < columnNamesRaw.size(); i++) {
|
||||
columnNames[i] = (String) columnNamesRaw.get(i);
|
||||
}
|
||||
return columnNames;
|
||||
}
|
||||
|
||||
public JSONArray getCompetitors() {
|
||||
return get("competitors");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public class DeviceMappingRequest {
|
||||
|
||||
private final JSONObject requestBody = new JSONObject();
|
||||
|
||||
+1
-1
@@ -104,7 +104,7 @@ public class RegattaApi {
|
||||
final String url = REGATTAS + "/" + regattaName + ADD_RACE_COLUMN_URL;
|
||||
final Map<String, String> queryParams = new TreeMap<>();
|
||||
queryParams.put("prefix", prefix);
|
||||
queryParams.put("numberOfRaces", numberOfRaces != null ? numberOfRaces.toString() : null);
|
||||
queryParams.put("numberofraces", numberOfRaces != null ? numberOfRaces.toString() : null);
|
||||
final JSONArray json = ctx.post(url, queryParams);
|
||||
final RaceColumn[] result = new RaceColumn[json.size()];
|
||||
for (int i = 0; i < json.size(); i++) {
|
||||
|
||||
+97
-12
@@ -5,6 +5,7 @@ import static com.sap.sailing.selenium.api.core.ApiContext.SERVER_CONTEXT;
|
||||
import static com.sap.sailing.selenium.api.core.ApiContext.createAdminApiContext;
|
||||
import static java.lang.System.currentTimeMillis;
|
||||
import static java.util.UUID.randomUUID;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
@@ -18,6 +19,7 @@ import com.sap.sailing.selenium.api.core.ApiContext;
|
||||
import com.sap.sailing.selenium.api.event.EventApi;
|
||||
import com.sap.sailing.selenium.api.event.LeaderboardApi;
|
||||
import com.sap.sailing.selenium.api.event.LeaderboardApi.DeviceMappingRequest;
|
||||
import com.sap.sailing.selenium.api.event.LeaderboardApi.Leaderboard;
|
||||
import com.sap.sailing.selenium.api.event.LeaderboardApi.TrackingTimes;
|
||||
import com.sap.sailing.selenium.api.regatta.Competitor;
|
||||
import com.sap.sailing.selenium.api.regatta.RaceColumn;
|
||||
@@ -42,19 +44,18 @@ public class LeaderboardApiTest extends AbstractSeleniumTest {
|
||||
public void testGetLeaderboardForCreatedEvent() {
|
||||
final ApiContext ctx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT);
|
||||
eventApi.createEvent(ctx, LEADERBOARD_NAME, BOATCLASSNAME, CLOSED, "default");
|
||||
JSONObject leaderBoard = leaderboardApi.getLeaderboard(ctx, LEADERBOARD_NAME);
|
||||
assertEquals("read: leaderboard.name is different", LEADERBOARD_NAME, leaderBoard.get("name"));
|
||||
assertEquals("read: leaderboard.displayName is different", LEADERBOARD_NAME, leaderBoard.get("displayName"));
|
||||
assertNotNull("read: leaderboard.resultTimepoint is missing", leaderBoard.get("resultTimepoint"));
|
||||
assertEquals("read: leaderboard.resultState is different", "Live", leaderBoard.get("resultState"));
|
||||
assertEquals("read: leaderboard.type is different", "RegattaLeaderboard", leaderBoard.get("type"));
|
||||
Leaderboard leaderBoard = leaderboardApi.getLeaderboard(ctx, LEADERBOARD_NAME);
|
||||
assertEquals("read: leaderboard.name is different", LEADERBOARD_NAME, leaderBoard.getName());
|
||||
assertEquals("read: leaderboard.displayName is different", LEADERBOARD_NAME, leaderBoard.getDisplayName());
|
||||
assertNotNull("read: leaderboard.resultTimepoint is missing", leaderBoard.getResultTimePoint());
|
||||
assertEquals("read: leaderboard.resultState is different", "Live", leaderBoard.getResultState());
|
||||
assertEquals("read: leaderboard.type is different", "RegattaLeaderboard", leaderBoard.getType());
|
||||
assertEquals("read: leaderboard.canBoatsOfCompetitorsChangePerRace is different", false,
|
||||
leaderBoard.get("canBoatsOfCompetitorsChangePerRace"));
|
||||
assertNull("read: leaderboard.maxCompetitorsCount should be null", leaderBoard.get("maxCompetitorsCount"));
|
||||
assertNull("read: leaderboard.scoringComment should be null", leaderBoard.get("scoringComment"));
|
||||
assertNull("read: leaderboard.lastScoringUpdate should be null", leaderBoard.get("lastScoringUpdate"));
|
||||
assertEquals("read: leaderboard.columnNames should be empty", 0,
|
||||
((JSONArray) leaderBoard.get("columnNames")).size());
|
||||
leaderBoard.canBoatsOfCompetitorsChangePerRace());
|
||||
assertNull("read: leaderboard.maxCompetitorsCount should be null", leaderBoard.getMaxCompetitorsCount());
|
||||
assertNull("read: leaderboard.scoringComment should be null", leaderBoard.getScoringComment());
|
||||
assertNull("read: leaderboard.lastScoringUpdate should be null", leaderBoard.getLastScoringUpdate());
|
||||
assertEquals("read: leaderboard.columnNames should be empty", 0, leaderBoard.getColumnNames().length);
|
||||
assertEquals("read: leaderboard.competitors should be empty", 0,
|
||||
((JSONArray) leaderBoard.get("competitors")).size());
|
||||
assertEquals("read: leaderboard.ShardingLeaderboardName is different",
|
||||
@@ -62,6 +63,90 @@ public class LeaderboardApiTest extends AbstractSeleniumTest {
|
||||
leaderBoard.get("ShardingLeaderboardName"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatingLeaderboard() {
|
||||
final ApiContext ctx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT);
|
||||
eventApi.createEvent(ctx, LEADERBOARD_NAME, BOATCLASSNAME, CLOSED, "default");
|
||||
final Leaderboard leaderboardOriginal = leaderboardApi.getLeaderboard(ctx, LEADERBOARD_NAME);
|
||||
|
||||
// updating resultDiscardingThresholds
|
||||
final int[] newResultDiscardingThresholds = new int[] { 1, 2, 3, 4, 5 };
|
||||
leaderboardApi.updateLeaderboard(ctx, LEADERBOARD_NAME, leaderboardOriginal.getDisplayName(),
|
||||
newResultDiscardingThresholds);
|
||||
|
||||
// reloading and check if updated
|
||||
Leaderboard leaderboardReloaded = leaderboardApi.getLeaderboard(ctx, LEADERBOARD_NAME);
|
||||
assertArrayEquals(newResultDiscardingThresholds,
|
||||
leaderboardReloaded.getDiscardIndexResultsStartingWithHowManyRaces());
|
||||
|
||||
// remove and check again
|
||||
final int[] newEmptyResultDiscardingThresholds = new int[] {};
|
||||
leaderboardApi.updateLeaderboard(ctx, LEADERBOARD_NAME, leaderboardOriginal.getDisplayName(),
|
||||
newEmptyResultDiscardingThresholds);
|
||||
leaderboardReloaded = leaderboardApi.getLeaderboard(ctx, LEADERBOARD_NAME);
|
||||
assertArrayEquals(newEmptyResultDiscardingThresholds,
|
||||
leaderboardReloaded.getDiscardIndexResultsStartingWithHowManyRaces());
|
||||
|
||||
// passing null should not change anything
|
||||
leaderboardApi.updateLeaderboard(ctx, LEADERBOARD_NAME, leaderboardOriginal.getDisplayName(),
|
||||
newResultDiscardingThresholds);
|
||||
leaderboardReloaded = leaderboardApi.getLeaderboard(ctx, LEADERBOARD_NAME);
|
||||
assertArrayEquals(newResultDiscardingThresholds,
|
||||
leaderboardReloaded.getDiscardIndexResultsStartingWithHowManyRaces());
|
||||
leaderboardApi.updateLeaderboard(ctx, LEADERBOARD_NAME, leaderboardOriginal.getDisplayName(), null);
|
||||
assertArrayEquals(newResultDiscardingThresholds,
|
||||
leaderboardReloaded.getDiscardIndexResultsStartingWithHowManyRaces());
|
||||
|
||||
// check that no other atributes have been changed
|
||||
assertEquals(leaderboardOriginal.getName(), leaderboardReloaded.getName());
|
||||
assertEquals(leaderboardOriginal.getDisplayName(), leaderboardReloaded.getDisplayName());
|
||||
|
||||
// update leaderboardDisplayName
|
||||
leaderboardApi.updateLeaderboard(ctx, LEADERBOARD_NAME, "another display name", null);
|
||||
leaderboardReloaded = leaderboardApi.getLeaderboard(ctx, LEADERBOARD_NAME);
|
||||
assertEquals("another display name", leaderboardReloaded.getDisplayName());
|
||||
|
||||
// check that no other atributes have been changed
|
||||
assertEquals(leaderboardOriginal.getName(), leaderboardReloaded.getName());
|
||||
assertArrayEquals(newResultDiscardingThresholds,
|
||||
leaderboardReloaded.getDiscardIndexResultsStartingWithHowManyRaces());
|
||||
|
||||
// update leaderboardDisplayName and resultDiscardingThresholds
|
||||
final int[] newResultDiscardingThresholds2 = new int[] {7, 8};
|
||||
leaderboardApi.updateLeaderboard(ctx, LEADERBOARD_NAME, "another display name 2", newResultDiscardingThresholds2);
|
||||
leaderboardReloaded = leaderboardApi.getLeaderboard(ctx, LEADERBOARD_NAME);
|
||||
assertEquals("another display name 2", leaderboardReloaded.getDisplayName());
|
||||
assertArrayEquals(newResultDiscardingThresholds2, newResultDiscardingThresholds2);
|
||||
|
||||
// update leaderboardDisplayName to null, this result in the name of the leaderboard
|
||||
leaderboardApi.updateLeaderboard(ctx, LEADERBOARD_NAME, /* leaderboardDisplayName */ null, newResultDiscardingThresholds2);
|
||||
leaderboardReloaded = leaderboardApi.getLeaderboard(ctx, LEADERBOARD_NAME);
|
||||
// if display name is null, the name of the leaderboard is returned
|
||||
assertEquals(leaderboardReloaded.getName(), leaderboardReloaded.getDisplayName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUpdatingLeaderboardDisplayNameToNull() {
|
||||
final ApiContext ctx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT);
|
||||
eventApi.createEvent(ctx, LEADERBOARD_NAME, BOATCLASSNAME, CLOSED, "default");
|
||||
|
||||
// update to not null values
|
||||
final int[] newResultDiscardingThresholds = new int[] { 1, 2, 3, 4, 5 };
|
||||
leaderboardApi.updateLeaderboard(ctx, LEADERBOARD_NAME, "newLeaderboardDisplayName",
|
||||
newResultDiscardingThresholds);
|
||||
Leaderboard leaderboardReloaded = leaderboardApi.getLeaderboard(ctx, LEADERBOARD_NAME);
|
||||
assertEquals("newLeaderboardDisplayName", leaderboardReloaded.getDisplayName());
|
||||
assertArrayEquals(newResultDiscardingThresholds,
|
||||
leaderboardReloaded.getDiscardIndexResultsStartingWithHowManyRaces());
|
||||
|
||||
// update to display name to null
|
||||
leaderboardApi.updateLeaderboardDisplayName(ctx, LEADERBOARD_NAME, null);
|
||||
leaderboardReloaded = leaderboardApi.getLeaderboard(ctx, LEADERBOARD_NAME);
|
||||
assertEquals(LEADERBOARD_NAME, leaderboardReloaded.getDisplayName());
|
||||
assertArrayEquals(newResultDiscardingThresholds,
|
||||
leaderboardReloaded.getDiscardIndexResultsStartingWithHowManyRaces());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeviceMappingStartAndEnd() throws Exception {
|
||||
final ApiContext ctx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT);
|
||||
|
||||
+2
-8
@@ -159,15 +159,14 @@ public class RegattaApiTest extends AbstractSeleniumTest {
|
||||
RaceColumn[] result = regattaApi.addRaceColumn(ctx, EVENT_NAME, "T", 5);
|
||||
assertEquals("read: racecolumn.seriesname is different", "Default", result[0].getSeriesName());
|
||||
assertEquals("read: racecolumn.racename is different", "T1", result[0].getRaceName());
|
||||
// assertEquals("read: racecolumn.seriesname is different", "Default", result[4].getSeriesName());
|
||||
// assertEquals("read: racecolumn.racename is different", "T5", result[4].getRaceName());
|
||||
assertEquals("read: racecolumn.seriesname is different", "Default", result[4].getSeriesName());
|
||||
assertEquals("read: racecolumn.racename is different", "T5", result[4].getRaceName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterExistingCompetitorWithSecret() {
|
||||
final AdminConsolePage adminConsole = goToPage(getWebDriver(), getContextRoot());
|
||||
adminConsole.goToLocalServerPanel().setSelfServiceServer(true);
|
||||
|
||||
SecurityApi securityApi = new SecurityApi();
|
||||
final ApiContext adminSecurityCtx = createAdminApiContext(getContextRoot(), ApiContext.SECURITY_CONTEXT);
|
||||
securityApi.createUser(adminSecurityCtx, "donald", "Donald Duck", null, "daisy0815");
|
||||
@@ -176,22 +175,17 @@ public class RegattaApiTest extends AbstractSeleniumTest {
|
||||
ApiContext.SECURITY_CONTEXT, "donald", "daisy0815");
|
||||
securityApi.createUser(adminSecurityCtx, "dagobert", "Dagobert Duck", null, "daisy1337");
|
||||
final ApiContext userOwningEventCtx = createApiContext(getContextRoot(), SERVER_CONTEXT, "dagobert", "daisy1337");
|
||||
|
||||
// TODO we currently can't just create a competitor using the API. This is only possible by using a temporary Event/Regatta.
|
||||
eventApi.createEvent(userWithCompetitorCtx, EVENT_NAME, BOAT_CLASS, CLOSED, "default");
|
||||
Competitor competitor = regattaApi.createAndAddCompetitor(userWithCompetitorCtx, EVENT_NAME, BOAT_CLASS, null, "donald", "USA");
|
||||
|
||||
// ensure the reader can actually see the competitor
|
||||
UserGroup group = usergroupApi.getUserGroupByName(userWithCompetitorAndSecurityCtx, "donald"+SecurityService.TENANT_SUFFIX);
|
||||
usergroupApi.addRoleToGroup(userWithCompetitorAndSecurityCtx, group.getGroupId(),
|
||||
UUID.fromString(/* sailing viewer role id */"c42948df-517b-45cb-9fa9-d1e79f18e115"), true);
|
||||
|
||||
// This is the event to register the existing competitor for
|
||||
Event eventToRegisterExistingCompetitor = eventApi.createEvent(userOwningEventCtx, OTHER_EVENT_NAME, BOAT_CLASS, CompetitorRegistrationType.OPEN_UNMODERATED, "default");
|
||||
regattaApi.addCompetitor(userWithCompetitorCtx, OTHER_EVENT_NAME, competitor.getId(), Optional.of(eventToRegisterExistingCompetitor.getSecret()));
|
||||
|
||||
Competitor[] competitors = regattaApi.getCompetitors(userOwningEventCtx, OTHER_EVENT_NAME);
|
||||
|
||||
assertEquals(1, competitors.length);
|
||||
assertEquals(competitor.getId(), competitors[0].getId());
|
||||
}
|
||||
|
||||
+40
@@ -124,6 +124,7 @@ import com.sap.sailing.server.hierarchy.SailingHierarchyOwnershipUpdater;
|
||||
import com.sap.sailing.server.interfaces.RacingEventService;
|
||||
import com.sap.sailing.server.operationaltransformation.RemoveAndUntrackRace;
|
||||
import com.sap.sailing.server.operationaltransformation.StopTrackingRace;
|
||||
import com.sap.sailing.server.operationaltransformation.UpdateLeaderboard;
|
||||
import com.sap.sailing.server.security.PermissionAwareRaceTrackingHandler;
|
||||
import com.sap.sse.InvalidDateException;
|
||||
import com.sap.sse.common.Bearing;
|
||||
@@ -268,6 +269,45 @@ public class LeaderboardsResource extends AbstractLeaderboardsResource {
|
||||
return jsonLeaderboard;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{name}/update")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
public Response updateLeaderboard(@PathParam("name") String leaderboardName, String json)
|
||||
throws ParseException, JsonDeserializationException {
|
||||
final Object requestBody = JSONValue.parseWithException(json);
|
||||
final JSONObject requestObject = Helpers.toJSONObjectSafe(requestBody);
|
||||
final Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName);
|
||||
if (leaderboard != null) {
|
||||
SecurityUtils.getSubject().checkPermission(
|
||||
SecuredDomainType.LEADERBOARD.getStringPermissionForObject(DefaultActions.UPDATE, leaderboard));
|
||||
String newLeaderboardDisplayName = leaderboard.getDisplayName();
|
||||
if (requestObject.containsKey("leaderboardDisplayName")) {
|
||||
newLeaderboardDisplayName = (String) requestObject.get("leaderboardDisplayName");
|
||||
}
|
||||
int[] resultDiscardingThresholds = null;
|
||||
if (requestObject.containsKey("resultDiscardingThresholds")) {
|
||||
final JSONArray resultDiscardingThresholdsRaw = (JSONArray) requestObject
|
||||
.get("resultDiscardingThresholds");
|
||||
if (resultDiscardingThresholdsRaw != null) {
|
||||
resultDiscardingThresholds = new int[resultDiscardingThresholdsRaw.size()];
|
||||
for (int i = 0; i < resultDiscardingThresholdsRaw.size(); i++) {
|
||||
resultDiscardingThresholds[i] = ((Long) resultDiscardingThresholdsRaw.get(i)).intValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
getService().apply(new UpdateLeaderboard(/* leaderboardName */ leaderboard.getName(),
|
||||
/* newLeaderboardName */ leaderboard.getName(), newLeaderboardDisplayName,
|
||||
resultDiscardingThresholds,
|
||||
leaderboard.getDefaultCourseArea() != null ? leaderboard.getDefaultCourseArea().getId() : null));
|
||||
} else {
|
||||
return Response.status(Status.NOT_FOUND).entity(
|
||||
"Could not find a leaderboard with name '" + StringEscapeUtils.escapeHtml(leaderboardName) + "'.")
|
||||
.type(MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
return Response.ok().header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8").build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Path("{name}/device_mappings/start")
|
||||
|
||||
@@ -189,6 +189,10 @@ name to make sure you get to the event you're interested in.
|
||||
<td><a href="leaderboardGetDoc.html">/api/v1/leaderboards/{name}</a></td>
|
||||
<td>Gets the details of a leaderboard</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="leaderboardUpdateDoc.html">/api/v1/leaderboards/{name}/update</a></td>
|
||||
<td>Modifies attributes of a leaderboard</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="leaderboardCheckinPostDoc.html">/api/v1/leaderboards/{name}/device_mappings/start</a></td>
|
||||
<td>Tracking App Device Checkin on leaderboard</td>
|
||||
|
||||
@@ -0,0 +1,68 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../sailing-fontface-1.0.cache.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sailing Analytics Webservices API Version 1.0</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sailing Analytics Webservices API Version 1.0</h1>
|
||||
<h2>URL: /api/v1/leaderboard/{name}/update</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>The service can modify some of the details of a leaderboard. Currently this allows to update the discarding thresholds of a leaderboard and the leaderboard display name.</p>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>REST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>Json</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Payload:</td>
|
||||
|
||||
<td>format: json<br /> *(optional) resultDiscardingThresholds: if
|
||||
parameter is absent or value is null, the discarding thresholds will
|
||||
not change. If passing an empty array the discarding thresholds
|
||||
will be cleared.<br/>
|
||||
*(optional) leaderboardDisplayName: if parameter is absent
|
||||
the displayName will not change.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td><a
|
||||
href="http://www.sapsailing.com/sailingserver/api/v1/leaderboards/505 Worlds 2013/update">http://www.sapsailing.com/sailingserver/api/v1/leaderboards/505
|
||||
Worlds 2013/update</a><br/>
|
||||
Request:
|
||||
<pre>
|
||||
{
|
||||
"resultDiscardingThresholds":[1,2],
|
||||
"leaderboardDisplayName":"505 Worlds 2014"
|
||||
}
|
||||
</pre>
|
||||
Response: Status 200
|
||||
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
+3
@@ -35,6 +35,7 @@ public interface AccessControlStore extends Named {
|
||||
void removeAclDenial(QualifiedObjectIdentifier idOfAccessControlledObject, UserGroup userGroup, String action);
|
||||
|
||||
void denyAclPermission(QualifiedObjectIdentifier idOfAccessControlledObject, UserGroup userGroup, String action);
|
||||
|
||||
void removeAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject);
|
||||
|
||||
Iterable<OwnershipAnnotation> getOwnerships();
|
||||
@@ -50,9 +51,11 @@ public interface AccessControlStore extends Named {
|
||||
|
||||
OwnershipAnnotation setOwnership(QualifiedObjectIdentifier idOfOwnedObject, User userOwner, UserGroup tenantOwner,
|
||||
String displayNameOfOwnedObject);
|
||||
|
||||
void removeOwnership(QualifiedObjectIdentifier idOfOwnedObject);
|
||||
|
||||
void clear();
|
||||
|
||||
void replaceContentsFrom(AccessControlStore newAccessControlStore);
|
||||
|
||||
/**
|
||||
|
||||
@@ -76,3 +76,4 @@ bearerToken = com.sap.sse.security.BearerTokenOrBasicOrFormAuthenticationFilter
|
||||
/api/restsecurity/usergroup = bearerToken
|
||||
/api/restsecurity/role = bearerToken
|
||||
/api/restsecurity/role/** = bearerToken
|
||||
/api/restsecurity/ownership/** = bearerToken
|
||||
@@ -93,6 +93,9 @@ public interface SecurityService extends ReplicableWithObjectInputStream<Replica
|
||||
AccessControlList overrideAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject,
|
||||
Map<UserGroup, Set<String>> permissionMap);
|
||||
|
||||
AccessControlList overrideAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject,
|
||||
Map<UserGroup, Set<String>> permissionMap, String displayNameOfAccessControlledObject);
|
||||
|
||||
/**
|
||||
* @param name The name of the user group to add
|
||||
*/
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.sap.sse.security.exceptions;
|
||||
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
import com.sap.sse.security.jaxrs.api.OwnershipResource;
|
||||
|
||||
/***
|
||||
* OwnershipException is used for {@link OwnershipResource} class to handle any exception in ownership Api's.
|
||||
*
|
||||
* @author Usman Ali
|
||||
*
|
||||
*/
|
||||
public class OwnershipException extends Exception {
|
||||
private static final long serialVersionUID = -7464597099374135405L;
|
||||
private Status status;
|
||||
|
||||
public OwnershipException(String message, Status status) {
|
||||
super(message);
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Status getStatus() {
|
||||
return status;
|
||||
}
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.sap.sse.security.exceptions.handler;
|
||||
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.ext.ExceptionMapper;
|
||||
import javax.ws.rs.ext.Provider;
|
||||
|
||||
import com.sap.sse.security.exceptions.OwnershipException;
|
||||
import com.sap.sse.security.jaxrs.ShiroAuthorizationExceptionTo401ResponseMapper;
|
||||
import com.sap.sse.security.model.GeneralResponse;
|
||||
|
||||
|
||||
/***
|
||||
* This class is used to handle {@link OwnershipException} thrown from any API of project.
|
||||
* @author Usman Ali
|
||||
*
|
||||
*/
|
||||
@Provider
|
||||
public class OwnershipExceptionHandler implements ExceptionMapper<OwnershipException> {
|
||||
private static final Logger logger = Logger.getLogger(ShiroAuthorizationExceptionTo401ResponseMapper.class.getName());
|
||||
@Override
|
||||
public Response toResponse(OwnershipException exception) {
|
||||
logger.log(Level.WARNING, "OwnershipException occurred in REST call: " + exception.getMessage());
|
||||
return Response.status(exception.getStatus()).entity(new GeneralResponse(false, exception.getMessage()).toString()).build();
|
||||
}
|
||||
}
|
||||
@@ -140,7 +140,6 @@ public class Activator implements BundleActivator {
|
||||
waitForUserStoreService(bundleContext);
|
||||
}
|
||||
context.registerService(ClearStateTestSupport.class, new ClearStateTestSupport() {
|
||||
|
||||
@Override
|
||||
public void clearState() throws Exception {
|
||||
Activator.this.clearState();
|
||||
@@ -149,7 +148,7 @@ public class Activator implements BundleActivator {
|
||||
}
|
||||
|
||||
/**
|
||||
* This method will clean the userStore and AccessControllStore, and reset the securityService. It will then
|
||||
* This method will clean the userStore and AccessControlStore, and reset the securityService. It will then
|
||||
* reinitialize them in the same fashion an empty server will do.
|
||||
*/
|
||||
protected void clearState() throws InterruptedException, UserGroupManagementException, UserManagementException {
|
||||
|
||||
@@ -523,6 +523,14 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
|
||||
return updateAccessControlList(idOfAccessControlledObject, permissionMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessControlList overrideAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject,
|
||||
Map<UserGroup, Set<String>> permissionMap, String displayNameOfAccessControlledObject) {
|
||||
accessControlStore.removeAccessControlList(idOfAccessControlledObject);
|
||||
accessControlStore.setEmptyAccessControlList(idOfAccessControlledObject, displayNameOfAccessControlledObject);
|
||||
return updateAccessControlList(idOfAccessControlledObject, permissionMap);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AccessControlList updateAccessControlList(QualifiedObjectIdentifier idOfAccessControlledObject,
|
||||
Map<UserGroup, Set<String>> permissionMap) {
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
package com.sap.sse.security.jaxrs.api;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.JSONValue;
|
||||
|
||||
import com.sap.sse.security.exceptions.OwnershipException;
|
||||
import com.sap.sse.security.jaxrs.AbstractSecurityResource;
|
||||
import com.sap.sse.security.model.GeneralResponse;
|
||||
import com.sap.sse.security.shared.AccessControlListAnnotation;
|
||||
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
|
||||
import com.sap.sse.security.shared.OwnershipAnnotation;
|
||||
import com.sap.sse.security.shared.QualifiedObjectIdentifier;
|
||||
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
|
||||
import com.sap.sse.security.shared.impl.QualifiedObjectIdentifierImpl;
|
||||
import com.sap.sse.security.shared.impl.User;
|
||||
import com.sap.sse.security.shared.impl.UserGroup;
|
||||
|
||||
@Path("/restsecurity/ownership")
|
||||
public class OwnershipResource extends AbstractSecurityResource {
|
||||
private static final String KEY_OBJECT_ID = "objectId";
|
||||
private static final String KEY_OBJECT_TYPE = "objectType";
|
||||
private static final String KEY_USERNAME = "username";
|
||||
private static final String KEY_GROUP_ID = "groupId";
|
||||
private static final String KEY_ACL = "acl";
|
||||
private static final String KEY_ACTIONS = "actions";
|
||||
private static final String KEY_DISPLAY_NAME = "displayName";
|
||||
|
||||
@Path("{objectType}/{typeRelativeObjectId}")
|
||||
@PUT
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
public Response setOwnership(@PathParam("objectType") String objectType,
|
||||
@PathParam("typeRelativeObjectId") String typeRelativeObjectId, String jsonBody) throws OwnershipException {
|
||||
final JSONObject json = (JSONObject) JSONValue.parse(jsonBody);
|
||||
QualifiedObjectIdentifier identifier = new QualifiedObjectIdentifierImpl((String) json.get(KEY_OBJECT_TYPE),
|
||||
new TypeRelativeObjectIdentifier((String) json.get(KEY_OBJECT_ID)));
|
||||
try {
|
||||
SecurityUtils.getSubject().checkPermission(identifier.getStringPermission(DefaultActions.CHANGE_OWNERSHIP));
|
||||
} catch (Exception ex) {
|
||||
throw new OwnershipException("Not permitted to change ownership.", Status.FORBIDDEN);
|
||||
}
|
||||
final OwnershipAnnotation existingOwnership = getService().getOwnership(identifier);
|
||||
final User user;
|
||||
if (json.containsKey(KEY_USERNAME)) {
|
||||
final String username = (String) json.get(KEY_USERNAME);
|
||||
if (username == null) {
|
||||
user = null;
|
||||
} else {
|
||||
user = getService().getUserByName(username);
|
||||
if (user == null) {
|
||||
// username provided but cannot be resolved
|
||||
throw new OwnershipException("User Not found", Status.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
user = existingOwnership == null ? null : existingOwnership.getAnnotation().getUserOwner();
|
||||
}
|
||||
final UserGroup userGroup;
|
||||
if (json.containsKey(KEY_GROUP_ID)) {
|
||||
final String userGroupIdAsString = (String) json.get(KEY_GROUP_ID);
|
||||
if (userGroupIdAsString == null) {
|
||||
userGroup = null;
|
||||
} else {
|
||||
userGroup = getService().getUserGroup(UUID.fromString(userGroupIdAsString));
|
||||
if (userGroup == null) {
|
||||
throw new OwnershipException("UserGroup Not found", Status.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
userGroup = existingOwnership == null ? null : existingOwnership.getAnnotation().getTenantOwner();
|
||||
}
|
||||
if (json.containsKey(KEY_DISPLAY_NAME)) {
|
||||
getService().setOwnership(identifier, user, userGroup, (String) json.get(KEY_DISPLAY_NAME));
|
||||
} else {
|
||||
getService().setOwnership(identifier, user, userGroup);
|
||||
}
|
||||
return Response.ok(new GeneralResponse(true, "Ownership changed successfully").toString()).build();
|
||||
}
|
||||
|
||||
@Path("{objectType}/{typeRelativeObjectId}")
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
public Response getOwnership(@PathParam("objectType") String objectType,
|
||||
@PathParam("typeRelativeObjectId") String typeRelativeObjectId) throws OwnershipException {
|
||||
QualifiedObjectIdentifier identifier = new QualifiedObjectIdentifierImpl(objectType,
|
||||
new TypeRelativeObjectIdentifier(typeRelativeObjectId));
|
||||
final OwnershipAnnotation existingOwnership = getService().getOwnership(identifier);
|
||||
final JSONObject result = new JSONObject();
|
||||
result.put(KEY_OBJECT_TYPE, objectType);
|
||||
result.put(KEY_OBJECT_ID, typeRelativeObjectId);
|
||||
result.put(KEY_GROUP_ID, existingOwnership == null || existingOwnership.getAnnotation().getTenantOwner() == null ? null : existingOwnership.getAnnotation().getTenantOwner().getId().toString());
|
||||
result.put(KEY_USERNAME, existingOwnership == null || existingOwnership.getAnnotation().getUserOwner() == null ? null : existingOwnership.getAnnotation().getUserOwner().getName());
|
||||
result.put(KEY_DISPLAY_NAME, existingOwnership == null || existingOwnership.getDisplayNameOfAnnotatedObject() == null ? null : existingOwnership.getDisplayNameOfAnnotatedObject());
|
||||
return Response.ok(result.toJSONString()).build();
|
||||
}
|
||||
|
||||
@Path("{objectType}/{typeRelativeObjectId}/acl")
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
public Response getAccessControlLists(@PathParam("objectType") String objectType,
|
||||
@PathParam("typeRelativeObjectId") String typeRelativeObjectId) throws OwnershipException {
|
||||
QualifiedObjectIdentifier identifier = new QualifiedObjectIdentifierImpl(objectType,
|
||||
new TypeRelativeObjectIdentifier(typeRelativeObjectId));
|
||||
final AccessControlListAnnotation acl = getService().getAccessControlList(identifier);
|
||||
final JSONObject result = new JSONObject();
|
||||
result.put(KEY_OBJECT_TYPE, objectType);
|
||||
result.put(KEY_OBJECT_ID, typeRelativeObjectId);
|
||||
result.put(KEY_DISPLAY_NAME, acl == null || acl.getDisplayNameOfAnnotatedObject() == null ? null : acl.getDisplayNameOfAnnotatedObject());
|
||||
final JSONArray actionsByUserGroup = new JSONArray();
|
||||
result.put(KEY_ACL, actionsByUserGroup);
|
||||
if (acl != null && acl.getAnnotation() != null) {
|
||||
for (final Entry<UserGroup, Set<String>> e : acl.getAnnotation().getActionsByUserGroup().entrySet()) {
|
||||
final JSONObject actionsForGroup = new JSONObject();
|
||||
actionsByUserGroup.add(actionsForGroup);
|
||||
actionsForGroup.put(KEY_GROUP_ID, e.getKey() == null ? null : e.getKey().getId().toString());
|
||||
final JSONArray actions = new JSONArray();
|
||||
actions.addAll(e.getValue());
|
||||
actionsForGroup.put(KEY_ACTIONS, actions);
|
||||
}
|
||||
}
|
||||
return Response.ok(result.toJSONString()).build();
|
||||
}
|
||||
|
||||
@Path("{objectType}/{typeRelativeObjectId}/acl")
|
||||
@PUT
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
public Response getAccessControlLists(@PathParam("objectType") String objectType,
|
||||
@PathParam("typeRelativeObjectId") String typeRelativeObjectId, String jsonBody) throws OwnershipException {
|
||||
final JSONObject json = (JSONObject) JSONValue.parse(jsonBody);
|
||||
final JSONArray actionsByUserGroupJson = (JSONArray) json.get(KEY_ACL);
|
||||
QualifiedObjectIdentifier identifier = new QualifiedObjectIdentifierImpl(objectType,
|
||||
new TypeRelativeObjectIdentifier(typeRelativeObjectId));
|
||||
try {
|
||||
SecurityUtils.getSubject().checkPermission(identifier.getStringPermission(DefaultActions.CHANGE_ACL));
|
||||
} catch (Exception ex) {
|
||||
throw new OwnershipException("Not permitted to change ownership.", Status.FORBIDDEN);
|
||||
}
|
||||
final Map<UserGroup, Set<String>> actionsByUserGroup = new HashMap<>();
|
||||
for (final Object o : actionsByUserGroupJson) {
|
||||
final JSONObject permissionsForGroup = (JSONObject) o;
|
||||
final String groupIdAsString = (String) permissionsForGroup.get(KEY_GROUP_ID);
|
||||
final UserGroup group;
|
||||
if (groupIdAsString == null) {
|
||||
group = null;
|
||||
} else {
|
||||
group = getService().getUserGroup(UUID.fromString(groupIdAsString));
|
||||
if (group == null) {
|
||||
throw new OwnershipException(String.format("UserGroup with ID %s Not found", groupIdAsString), Status.BAD_REQUEST);
|
||||
}
|
||||
}
|
||||
final JSONArray actions = (JSONArray) permissionsForGroup.get(KEY_ACTIONS);
|
||||
final Set<String> actionsForGroup = new HashSet<>();
|
||||
for (final Object action : actions) {
|
||||
actionsForGroup.add(action.toString());
|
||||
}
|
||||
actionsByUserGroup.put(group, actionsForGroup);
|
||||
}
|
||||
if (json.containsKey(KEY_DISPLAY_NAME)) {
|
||||
final String displayName = (String) json.get(KEY_DISPLAY_NAME);
|
||||
getService().overrideAccessControlList(identifier, actionsByUserGroup, displayName);
|
||||
} else {
|
||||
getService().overrideAccessControlList(identifier, actionsByUserGroup);
|
||||
}
|
||||
return Response.ok(new GeneralResponse(true, "ACL changed successfully").toString()).build();
|
||||
}
|
||||
}
|
||||
+3
-1
@@ -5,6 +5,7 @@ import java.util.Set;
|
||||
|
||||
import javax.ws.rs.core.Application;
|
||||
|
||||
import com.sap.sse.security.exceptions.handler.OwnershipExceptionHandler;
|
||||
import com.sap.sse.security.jaxrs.ShiroAuthorizationExceptionTo401ResponseMapper;
|
||||
|
||||
public class RestApiApplication extends Application {
|
||||
@@ -13,9 +14,10 @@ public class RestApiApplication extends Application {
|
||||
classes.add(SecurityResource.class);
|
||||
classes.add(UserGroupResource.class);
|
||||
classes.add(RoleResource.class);
|
||||
|
||||
classes.add(OwnershipResource.class);
|
||||
// exception mapper
|
||||
classes.add(ShiroAuthorizationExceptionTo401ResponseMapper.class);
|
||||
classes.add(OwnershipExceptionHandler.class);
|
||||
return classes;
|
||||
}
|
||||
}
|
||||
@@ -268,19 +268,17 @@ public class UserGroupResource extends AbstractSecurityResource {
|
||||
public Response addRoleToUserGroup(@PathParam(KEY_GROUP_ID) String userGroupId,
|
||||
@PathParam(KEY_ROLE_ID) String roleIdString, String body) {
|
||||
Response response;
|
||||
|
||||
try {
|
||||
final UUID groupId = UUID.fromString(userGroupId);
|
||||
final UserGroup usergroup = getService().getUserGroup(groupId);
|
||||
|
||||
final UUID roleId = UUID.fromString(roleIdString);
|
||||
final UserGroup usergroup = getService().getUserGroup(groupId);
|
||||
if (usergroup == null) {
|
||||
response = Response.status(Status.BAD_REQUEST).entity("Usergroup with this name does not exist.")
|
||||
.build();
|
||||
response = Response.status(Status.BAD_REQUEST).entity(
|
||||
String.format("Usergroup with ID %s does not exist.", userGroupId)).build();
|
||||
} else {
|
||||
final RoleDefinition role = getService().getRoleDefinition(roleId);
|
||||
if (role == null) {
|
||||
response = Response.status(Status.BAD_REQUEST).entity("Role with this id does not exist.").build();
|
||||
response = Response.status(Status.BAD_REQUEST).entity(String.format("Role with ID %s does not exist.", roleId)).build();
|
||||
} else {
|
||||
if (getService().hasCurrentUserUpdatePermission(usergroup)
|
||||
&& getService().hasCurrentUserReadPermission(role)) {
|
||||
@@ -291,15 +289,16 @@ public class UserGroupResource extends AbstractSecurityResource {
|
||||
if (!getService().hasCurrentUserMetaPermissionsOfRoleDefinitionWithQualification(role,
|
||||
new Ownership(null, usergroup))) {
|
||||
response = Response.status(Status.UNAUTHORIZED)
|
||||
.entity(String.format("Not permitted to add role %s to group", role.getName()))
|
||||
.build();
|
||||
.entity(String.format("Not permitted to add role %s to group %s",
|
||||
role.getName(), usergroup.getName())).build();
|
||||
} else {
|
||||
getService().putRoleDefinitionToUserGroup(usergroup, role, roleForAll);
|
||||
response = Response.ok().build();
|
||||
}
|
||||
} else {
|
||||
response = Response.status(Status.BAD_REQUEST)
|
||||
.entity("Role was already added to the group.").build();
|
||||
.entity(String.format("Role %s was already added to group %s.", role.getName(),
|
||||
usergroup.getName())).build();
|
||||
}
|
||||
} else {
|
||||
response = Response.status(Status.UNAUTHORIZED).build();
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.sap.sse.security.model;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/***
|
||||
* This Class will be used to send general response in JSON format.
|
||||
* @author Usman Ali
|
||||
*
|
||||
*/
|
||||
public class GeneralResponse implements Serializable{
|
||||
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private boolean responseStatus;
|
||||
private String responseMessage;
|
||||
|
||||
public GeneralResponse(boolean responseStatus, String responseMessage) {
|
||||
super();
|
||||
this.responseStatus = responseStatus;
|
||||
this.responseMessage = responseMessage;
|
||||
}
|
||||
|
||||
public boolean isResponseStatus() {
|
||||
return responseStatus;
|
||||
}
|
||||
public void setResponseStatus(boolean responseStatus) {
|
||||
this.responseStatus = responseStatus;
|
||||
}
|
||||
public String getResponseMessage() {
|
||||
return responseMessage;
|
||||
}
|
||||
public void setResponseMessage(String responseMessage) {
|
||||
this.responseMessage = responseMessage;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return " {\"responseStatus\":\"" + responseStatus + "\", \"responseMessage\":\"" + responseMessage + "\"}";
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -62,6 +62,10 @@ be <code>http://sapsailing.com/security/api/restsecurity/login</code>.
|
||||
<td><a href="remove_access_token.html">/api/restsecurity/remove_access_token</a></td>
|
||||
<td>Remove an existing access token, rendering it invalid for authenticating to the server</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="ownership.html">/api/restsecurity/ownership</a></td>
|
||||
<td>Change Object ownership</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</body>
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
|
||||
<link href='../../../../fontface.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link href='../../../main.css' media='screen' rel='stylesheet' title='Default stylesheet' type='text/css' />
|
||||
<link rel='icon' href='../../../../sap.ico' type='image/x-icon'>
|
||||
<title>SAP Sponsorships Engine Security Webservices API</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>SAP Sponsorships Engine Security Webservices API</h1>
|
||||
<h2>URL: /api/restsecurity/onwership</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>The service obtains or changes the ownership of object, or obtains or changes access control list (ACL) properties of an object.</p>
|
||||
<br/>
|
||||
|
||||
<b>Change Ownership</b>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Request Method:</td>
|
||||
<td>PUT /{objectType}/{typeRelativeObjectId}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>a JSON document of the following format<br>
|
||||
<pre>{ "responseStatus": "true", "responseMessage": "Ownership changed successfully"}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Parameter (path and body):</td>
|
||||
<td>
|
||||
<div>typeRelativeObjectId</div> Unique Object id.
|
||||
<div>objectType</div> Object type represents the type of object to change the ownership.
|
||||
<div>groupId</div> GroupId to assign given group to ownership of object object. If the property is missing in the body,
|
||||
the group ownership is not touched. If it is null, the group ownership is reset to null.
|
||||
<div>username</div> Username to search the user in order to attach it with given Object ownership. If the property is missing in the body,
|
||||
the user ownership is not touched. If it is null, the user ownership is reset to null.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td><code>curl -X POST -d '{
|
||||
"groupId":"admin-tenant",
|
||||
"username":"admin"
|
||||
}' -H 'Content-type: application/json' "http://admin:admin@127.0.0.1:8888/security/api/restsecurity/ownership/USER_GROUP/427ebd42-ac69-4f74-a171-376d9bea9457"</code><br>
|
||||
Will provide a JSON document as explained above.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Request Method:</td>
|
||||
<td>GET /{objectType}/{typeRelativeObjectId}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>a JSON document of the following format<br>
|
||||
<pre>{"objectType":"USER_GROUP","objectId":"82832851-07ac-47ee-9ddf-6f4f9eaa7823","groupId":"82832851-07ac-47ee-9ddf-6f4f9eaa7823","username":"admin"}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Parameter (path)</td>
|
||||
<td>
|
||||
<div>objectType</div> Object type represents the type of object to change the ownership.
|
||||
<div>objectId</div> Unique Object id.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td><code>curl "http://127.0.0.1:8888/security/api/restsecurity/ownership/USER_GROUP/82832851-07ac-47ee-9ddf-6f4f9eaa7823"</code><br>
|
||||
Will provide a JSON document as explained above.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Request Method:</td>
|
||||
<td>GET /{objectType}/{typeRelativeObjectId}/acl</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>a JSON document of the following format<br>
|
||||
<pre>{"objectType":"USER_GROUP","objectId":"82832851-07ac-47ee-9ddf-6f4f9eaa7823","displayName": null,"acl":[{"groupId":null,"actions":["READ","!DELETE"]},{"groupId":"82832851-07ac-47ee-9ddf-6f4f9eaa7823","actions":["READ","!UPDATE"]}]}
|
||||
</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Parameter (path)</td>
|
||||
<td>
|
||||
<div>objectType</div> Object type represents the type of object to request the ACL information for
|
||||
<div>objectId</div> Unique Object id.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td><code>curl -i -X GET "http://admin:admin@127.0.0.1:8888/security/api/restsecurity/ownership/USER_GROUP/82832851-07ac-47ee-9ddf-6f4f9eaa7823/acl"</code><br>
|
||||
Will provide a JSON document as explained above.</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>Request Method:</td>
|
||||
<td>PUT /{objectType}/{typeRelativeObjectId}/acl</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>a JSON document of the following format<br>
|
||||
<pre>{ "responseStatus": "true", "responseMessage": "Ownership changed successfully"}</pre>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Parameter (path, body)</td>
|
||||
<td>
|
||||
<div>objectType</div> Object type represents the type of object to request the ACL information for
|
||||
<div>objectId</div> Unique Object id.
|
||||
<div>The body must contain a document as the one returned by the GET method, only that the objectType and objectId fields are not required in
|
||||
the body and will be ignored as these parameters are expected to be provided in the path.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td><code>curl -i -X PUT -H 'Content-Type: application/json' -d '{"objectType":"USER_GROUP","objectId":"82832851-07ac-47ee-9ddf-6f4f9eaa7823","displayName":"The Admin Tenant ACL","acl":[{"groupId":null,"actions":["READ","!UPDATE"]},{"groupId":"82832851-07ac-47ee-9ddf-6f4f9eaa7823","actions":["READ","!DELETE"]}]}' "http://admin:admin@127.0.0.1:8888/security/api/restsecurity/ownership/USER_GROUP/82832851-07ac-47ee-9ddf-6f4f9eaa7823/acl"</code><br>
|
||||
Will provide a JSON document as explained above.</td>
|
||||
</tr>
|
||||
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -113,7 +113,7 @@
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
<b>Delete a user from a UserGroup</b>
|
||||
<b>Delete a user from a UserGroup</b>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Request Method:</td>
|
||||
@@ -151,12 +151,12 @@
|
||||
<td>
|
||||
<div>groupId (Path)</div> Unique name of the UserGroup to add the role to.
|
||||
<div>roleId (Path)</div> Unique ID of the role to add to the UserGroup.
|
||||
<div>roleAssociated (JSON Body)</div> True or false, depending whether the role's permissions are applied regardless of the user performing the access, only requiring the object access to be owned by this group; otherwise, the role's permissions are only granted if the object accessed is owned by this group and the user performing the access belongs to this group.
|
||||
<div>forAll (JSON Body)</div> True or false, depending whether the role's permissions are applied regardless of the user performing the access, only requiring the object access to be owned by this group; otherwise, the role's permissions are only granted if the object accessed is owned by this group and the user performing the access belongs to this group.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td><code>curl -X PUT -d '{"roleAssociated": true}' "127.0.0.1:8888/security/api/restsecurity/usergroup/9201d7cb-969e-4975-8cf1-32972bb2c8ba/role/29914406-b0fc-43cd-acc7-62a83931f587"</code><br></td>
|
||||
<td><code>curl -X PUT -d '{"forAll": true}' "127.0.0.1:8888/security/api/restsecurity/usergroup/9201d7cb-969e-4975-8cf1-32972bb2c8ba/role/29914406-b0fc-43cd-acc7-62a83931f587"</code><br></td>
|
||||
</tr>
|
||||
</table>
|
||||
<br/>
|
||||
|
||||
Reference in new issue
Block a user