mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-08 20:12:23 +00:00
Merge remote-tracking branch 'origin/master' into bug5030
This commit is contained in:
commit
cfb8ad2b4e
44 files changed
+1736
-85
No files matched your search
+5
-1
@@ -27,6 +27,10 @@ public abstract class AbstractDistance implements Distance {
|
||||
return meters > otherMeters ? 1 : meters == otherMeters ? 0 : -1;
|
||||
}
|
||||
|
||||
private Speed inSeconds(double seconds) {
|
||||
return new KilometersPerHourSpeedImpl(getKilometers() * 3600 / seconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Speed inTime(long milliseconds) {
|
||||
return new KilometersPerHourSpeedImpl(getKilometers() * 1000. * 3600. / milliseconds);
|
||||
@@ -34,7 +38,7 @@ public abstract class AbstractDistance implements Distance {
|
||||
|
||||
@Override
|
||||
public Speed inTime(Duration duration) {
|
||||
return inTime(duration.asMillis());
|
||||
return inSeconds(duration.asSeconds());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+18
-1
@@ -13,6 +13,7 @@ import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsDurationImpl;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.impl.SecondsDurationImpl;
|
||||
|
||||
public class DurationTest {
|
||||
@Test
|
||||
@@ -88,7 +89,7 @@ public class DurationTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperations() {
|
||||
public void testOperationsMilliseconds() {
|
||||
Duration someSeconds = new MillisecondsDurationImpl(2400).minus(1000);
|
||||
assertEquals(0, someSeconds.asDays(), 0.1);
|
||||
assertEquals(0, someSeconds.asHours(), 0.1);
|
||||
@@ -102,6 +103,22 @@ public class DurationTest {
|
||||
assertEquals(3600*25, oneDay.asSeconds(), 0.1);
|
||||
assertEquals(3600*25*1000, oneDay.asMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOperationsSeconds() {
|
||||
Duration someSeconds = new SecondsDurationImpl(2.4).minus(1000);
|
||||
assertEquals(0, someSeconds.asDays(), 0.1);
|
||||
assertEquals(0, someSeconds.asHours(), 0.1);
|
||||
assertEquals(0, someSeconds.asMinutes(), 0.1);
|
||||
assertEquals(1.4, someSeconds.asSeconds(), 0.1);
|
||||
assertEquals(1400, someSeconds.asMillis(), 0.1);
|
||||
Duration oneDay = Duration.ONE_DAY.plus(Duration.ONE_HOUR);
|
||||
assertEquals(25, oneDay.asHours(), 0.001);
|
||||
assertEquals(1, oneDay.asDays(), 0.01);
|
||||
assertEquals(25*60, oneDay.asMinutes(), 0.1);
|
||||
assertEquals(3600*25, oneDay.asSeconds(), 0.1);
|
||||
assertEquals(3600*25*1000, oneDay.asMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void compare() {
|
||||
|
||||
+8
@@ -4,6 +4,14 @@
|
||||
<div id="mainContent">
|
||||
<h4 class="articleHeadline">What's New - SAP Sailing Race Manager</h4>
|
||||
<div class="innerContent">
|
||||
<h5 class="articleSubheadline">July 2019</h5>
|
||||
<ul class="bulletList">
|
||||
<li>The app no longer remembers the last entered duration when setting a protest time.
|
||||
The protest duration can now be configured correctly in the app settings.
|
||||
Please consider that a protest duration in the specific regatta configuration is preferred to
|
||||
the protest duration in the default regatta configuration.</li>
|
||||
</ul>
|
||||
|
||||
<h5 class="articleSubheadline">May 2019</h5>
|
||||
<ul class="bulletList">
|
||||
<li>The Course editor now works correctly when managing more than one regatta.
|
||||
|
||||
+8
-1
@@ -357,7 +357,14 @@ public class AdminConsoleEntryPoint extends AbstractSailingEntryPoint
|
||||
|
||||
ResultImportUrlsManagementPanel resultImportUrlsManagementPanel = new ResultImportUrlsManagementPanel(
|
||||
getSailingService(), getUserService(), this, getStringMessages());
|
||||
panel.addToTabPanel(connectorsTabPanel, new DefaultRefreshableAdminConsolePanel<ResultImportUrlsManagementPanel>(resultImportUrlsManagementPanel),
|
||||
panel.addToTabPanel(connectorsTabPanel,
|
||||
new DefaultRefreshableAdminConsolePanel<ResultImportUrlsManagementPanel>(
|
||||
resultImportUrlsManagementPanel) {
|
||||
@Override
|
||||
public void refreshAfterBecomingVisible() {
|
||||
resultImportUrlsManagementPanel.refreshUrlList();
|
||||
}
|
||||
},
|
||||
getStringMessages().resultImportUrls(),
|
||||
SecuredDomainType.RESULT_IMPORT_URL.getPermission(DefaultActions.values()));
|
||||
StructureImportManagementPanel structureImportUrlsManagementPanel = new StructureImportManagementPanel(
|
||||
|
||||
+1
-1
@@ -107,7 +107,7 @@ public class ResultImportUrlsManagementPanel extends FlowPanel {
|
||||
return result;
|
||||
}
|
||||
|
||||
private void refreshUrlList() {
|
||||
public void refreshUrlList() {
|
||||
String selectedProviderName = getSelectedProviderName();
|
||||
if (selectedProviderName != null) {
|
||||
sailingService.getResultImportUrls(selectedProviderName, new AsyncCallback<List<String>>() {
|
||||
|
||||
+9
-1
@@ -948,6 +948,7 @@ raceTimeUpwindTooltip=Tiempo total de ceñida recorrido en esta carrera
|
||||
noCustomGrouperScriptTextError=El script de grupo está vacío
|
||||
noGrouperSelectedError=No se ha seleccionado ningún tipo de agrupador
|
||||
dataMining=Minería de datos
|
||||
changeOrientation=Modificar orientación
|
||||
hideToolbar=Ocultar barra de herramientas
|
||||
showSeriesLeaderboards=Mostrar tablas de clasificación de las series
|
||||
showOverallLeaderboard=Mostrar tabla de clasificación general
|
||||
@@ -1051,8 +1052,10 @@ resultFromIgtimiWindImport=Se ha importado el número siguiente de correcciones
|
||||
timeToStart={0} hasta la salida
|
||||
timeSinceStart={0} desde la salida
|
||||
distanceToLine=Distancia hasta la línea de salida
|
||||
TWATooltip=Ángulo entre la dirección de los competidores y el viento
|
||||
TWATooltip=El ángulo entre el rumbo de los competidores sobre el fondo y el viento, muestra valores negativos que implican viento de babor, valores positivos que implican viento de estribor.
|
||||
absoluteTWATooltip=El ángulo positivo entre el rumbo de los competidores sobre el fondo y el viento, sin signo +/-
|
||||
TWA=Ángulo real del viento
|
||||
absoluteTWA=Ángulo real absoluto del viento
|
||||
showBoatClassChartsLabel=También puede visualizar el diagrama global para las clases de embarcación disponibles.
|
||||
showDiagram=Mostrar diagrama
|
||||
rerunQueryAfterRefreshTooltip=Vuelve a ejecutar la consulta después de que se hayan refrescado las tablas.
|
||||
@@ -1927,6 +1930,7 @@ finishingTime=Hora de llegada
|
||||
finishTimeString=Hora de fin
|
||||
setFinishingAndFinishTime=Fijar hora de llegada y de fin
|
||||
finishTimeMustBeAtOrAfterFinishingTime=Hora de fin debe ser a la hora de llegada o posterior
|
||||
youHaveToEnterAFinishingTimeIfYouEnterAFinishedTime=Debe indicar la hora de llegada si indica la hora de fin
|
||||
boatNameMustNotBeEmpty=Proporcione un nombre de embarcación que no esté en blanco
|
||||
errorUpdatingExpeditionDeviceConfiguration=Error al actualizar la configuración del dispositivo con la expedición "{0}": {1}
|
||||
doYouReallyWantToRemoveExpeditionDeviceConfiguration=¿Desea realmente eliminar la configuración del dispositivo con la expedición "{0}"?
|
||||
@@ -2255,3 +2259,7 @@ editSwissTimingConnections=Tratar conexión SwissTiming
|
||||
filterSwissTimingConnections=Filtrar conexiones SwissTiming
|
||||
addSwissTimingConnection=Añadir conexión SwissTiming
|
||||
updateURL=Actualizar URL
|
||||
startProcedure=Iniciar procedimiento
|
||||
startMode=Iniciar modo
|
||||
gateOpeningTime=Horario de apertura de puerta
|
||||
protestTimeInMinutes=Tiempo para protestar en minutos
|
||||
+9
-1
@@ -948,6 +948,7 @@ raceTimeUpwindTooltip=Durée totale de navigation dans le lit du vent dans cette
|
||||
noCustomGrouperScriptTextError=Script de regroupement vide
|
||||
noGrouperSelectedError=Aucun type de regroupement sélectionné
|
||||
dataMining=Exploration de données
|
||||
changeOrientation=Modifier orientation
|
||||
hideToolbar=Masquer barre d''outils
|
||||
showSeriesLeaderboards=Afficher palmarès de la série
|
||||
showOverallLeaderboard=Afficher palmarès général
|
||||
@@ -1051,8 +1052,10 @@ resultFromIgtimiWindImport=Le nombre suivant de points vent ont été importés
|
||||
timeToStart={0} jusqu''au départ
|
||||
timeSinceStart={0} depuis le départ
|
||||
distanceToLine=Distance jusqu''à la ligne de départ
|
||||
TWATooltip=L''angle entre la direction du concurrent et le vent.
|
||||
TWATooltip=L''angle entre la route fond des concurrents et le vent, accompagné d''un signe : les valeurs négatives indiquent que le vent vient de bâbord, les valeurs positives indiquent que le vent vient de tribord.
|
||||
absoluteTWATooltip=L''angle positif entre la route fond des concurrents et le vent, sans signe +/-.
|
||||
TWA=Angle du vent réel
|
||||
absoluteTWA=Angle du vent réel absolu
|
||||
showBoatClassChartsLabel=Vous pouvez également afficher le diagramme global pour les catégories de bateau disponibles.
|
||||
showDiagram=Afficher diagramme
|
||||
rerunQueryAfterRefreshTooltip=Réexécute la requête après l''actualisation des tables.
|
||||
@@ -1927,6 +1930,7 @@ finishingTime=Heure d''arrivée du premier
|
||||
finishTimeString=Heure de fermeture de la ligne d''arrivée
|
||||
setFinishingAndFinishTime=Définir heure d''arrivée du premier et de fermeture de la ligne d''arrivée
|
||||
finishTimeMustBeAtOrAfterFinishingTime=L''heure de fermeture de la ligne d''arrivée doit être égale ou ultérieure à l''heure d''arrivée du premier.
|
||||
youHaveToEnterAFinishingTimeIfYouEnterAFinishedTime=Vous devez saisir une heure d''arrivée du premier si vous saisissez une heure de fermeture de la ligne d''arrivée.
|
||||
boatNameMustNotBeEmpty=Veuillez indiquer un nom de bateau.
|
||||
errorUpdatingExpeditionDeviceConfiguration=Erreur lors de la mise à jour de la configuration du terminal Expedition "{0}" : {1}
|
||||
doYouReallyWantToRemoveExpeditionDeviceConfiguration=Voulez-vous vraiment supprimer la configuration du terminal Expedition "{0}" ?
|
||||
@@ -2255,3 +2259,7 @@ editSwissTimingConnections=Modifier connexion SwissTiming
|
||||
filterSwissTimingConnections=Filtrer connexions SwissTiming
|
||||
addSwissTimingConnection=Ajouter connexion SwissTiming
|
||||
updateURL=Mettre à jour URL
|
||||
startProcedure=Procédure de départ
|
||||
startMode=Mode de départ
|
||||
gateOpeningTime=Heure d''ouverture de la porte
|
||||
protestTimeInMinutes=Durée de réclamation en minutes
|
||||
+13
-5
@@ -339,7 +339,7 @@ notAvailable=Non disponibile
|
||||
noGroupSelected=Nessun gruppo selezionato
|
||||
combinedWindSourceTypeName=Combinato
|
||||
legMiddleWindSourceTypeName=Metà della tratta
|
||||
courseBasedWindSourceTypeName=Progettazione rotta
|
||||
courseBasedWindSourceTypeName=Progettazione della rotta
|
||||
trackBasedEstimationWindSourceTypeName=Stima
|
||||
windSensorWindSourceTypeName=Misurato
|
||||
webWindSourceTypeName=Manuale
|
||||
@@ -948,6 +948,7 @@ raceTimeUpwindTooltip=Tempo totale di navigazione di bolina in questa gara
|
||||
noCustomGrouperScriptTextError=Script membro vuoto
|
||||
noGrouperSelectedError=Nessun tipo di membro selezionato
|
||||
dataMining=Estrazione dati
|
||||
changeOrientation=Modifica orientamento
|
||||
hideToolbar=Nascondi barra degli strumenti
|
||||
showSeriesLeaderboards=Mostra classifiche serie
|
||||
showOverallLeaderboard=Mostra classifica generale
|
||||
@@ -1051,8 +1052,10 @@ resultFromIgtimiWindImport=Per le gare elencate è stato importato il seguente n
|
||||
timeToStart={0} all’inizio
|
||||
timeSinceStart={0} dall’inizio
|
||||
distanceToLine=Distanza fino alla linea di partenza
|
||||
TWATooltip=L’angolo tra la direzione dei concorrenti e il vento
|
||||
TWATooltip=L’angolo tra la rotta effettiva al fondo dei concorrenti e il vento tracciato, dove i valori negativi indicano il vento da sinistra e quelli positivi il vento da destra
|
||||
absoluteTWATooltip=L''angolo positivo tra la rotta effettiva al fondo dei concorrenti e il vento, senza segno positivo o negativo
|
||||
TWA=Angolo del vento reale
|
||||
absoluteTWA=Angolo assoluto del vento reale
|
||||
showBoatClassChartsLabel=È anche possibile visualizzare il diagramma complessivo delle classi di barca disponibili.
|
||||
showDiagram=Mostra diagramma
|
||||
rerunQueryAfterRefreshTooltip=Riesegue la query dopo che le tabelle sono state aggiornate.
|
||||
@@ -1927,6 +1930,7 @@ finishingTime=Ora conclusione
|
||||
finishTimeString=Ora di fine
|
||||
setFinishingAndFinishTime=Imposta ora conclusione e ora di fine
|
||||
finishTimeMustBeAtOrAfterFinishingTime=L’ora di fine deve essere uguale o successiva all’ora di conclusione
|
||||
youHaveToEnterAFinishingTimeIfYouEnterAFinishedTime=Se si inserisce un''ora di fine occorre indicare un''ora di conclusione
|
||||
boatNameMustNotBeEmpty=Fornisci un nome barca non vuoto
|
||||
errorUpdatingExpeditionDeviceConfiguration=Errore nell’aggiornamento della configurazione del dispositivo Expedition "{0}": {1}
|
||||
doYouReallyWantToRemoveExpeditionDeviceConfiguration=Confermare l''eliminazione della configurazione del dispositivo Expedition "{0}"?
|
||||
@@ -2001,9 +2005,9 @@ raceNameIsRequired=Deve essere indicato un nome per la gara suddivisa.
|
||||
raceNameIsAlreadyUsed=Il nome indicato è già in uso
|
||||
errorWhilePreparingToSliceARace=Errore nella preparazione della suddivisione gara
|
||||
errorWhileSlicingARace=Errore nella suddivisione della gara
|
||||
windFinderWindSourceTypeName=
|
||||
windFinderWindSourceTypeName=Windfinder
|
||||
windFinderWindSourceTypeTooltip=Vento misurato da uno o più punti di www.windfinder.com
|
||||
windFinder=
|
||||
windFinder=Windfinder
|
||||
enterTagsForTheVideo=Inserisci i tag per il video
|
||||
enterIdOfWindFinderReviewedSpotCollection=Inserisci l''ID di una collezione di punti WindFinder rivista, es. "schilksee"
|
||||
windFinderSpotCollectionsList=Collezioni punti WindFinder
|
||||
@@ -2128,7 +2132,7 @@ automaticResizeNeedsStorageService=Per il ridimensionamento automatico occorre u
|
||||
qrCodeBoatInviteTitle=Sei invitato a tracciare la barca {0} per la regata {1}
|
||||
qrCodeMarkInviteTitle=Sei invitato a tracciare il segnale {0} per la regata {1}
|
||||
qrCodeCompetitorInviteTitle=Benvenuto {0} alla regata {1}
|
||||
qrCodeTitleBouy=Benvenuto come {0}
|
||||
qrCodeTitleBouy=Benvenuto come posaboe per la regata {0}
|
||||
qrCodeTitle=Benvenuto alla regata {0}
|
||||
qrCodeSubtitle={0} - {1}
|
||||
qrCodeTitleOpenRegatta=Benvenuto alla regata pubblica {0}
|
||||
@@ -2255,3 +2259,7 @@ editSwissTimingConnections=Modifica connessione SwissTiming
|
||||
filterSwissTimingConnections=Filtra connessioni SwissTiming
|
||||
addSwissTimingConnection=Aggiungi connessione SwissTiming
|
||||
updateURL=Aggiorna URL
|
||||
startProcedure=Procedura di partenza
|
||||
startMode=Modalità di partenza
|
||||
gateOpeningTime=Ora di apertura del cancello
|
||||
protestTimeInMinutes=Tempo per protesta (minuti)
|
||||
+9
-1
@@ -948,6 +948,7 @@ raceTimeUpwindTooltip=このレースでアップウィンドで移動した合
|
||||
noCustomGrouperScriptTextError=分類スクリプトが空です
|
||||
noGrouperSelectedError=分類タイプが選択されていません
|
||||
dataMining=データマイニング
|
||||
changeOrientation=方向の変更
|
||||
hideToolbar=ツールバー非表示
|
||||
showSeriesLeaderboards=シリーズリーダーボード表示
|
||||
showOverallLeaderboard=全体リーダーボード表示
|
||||
@@ -1051,8 +1052,10 @@ resultFromIgtimiWindImport=一覧されたレースに対して次の数のフ
|
||||
timeToStart=スタートまで {0}
|
||||
timeSinceStart=スタートから {0}
|
||||
distanceToLine=スタートラインまでの距離
|
||||
TWATooltip=競技者の方向と風の間のアングル
|
||||
TWATooltip=競技者の対地コースと風の間のアングル (符号あり、マイナスの値は左舷からの風、プラスの値は右舷からの風を意味)
|
||||
absoluteTWATooltip=競技者の対地コースと風の間のアングル (+/- 符号のないプラスの値)
|
||||
TWA=真の風角度
|
||||
absoluteTWA=絶対真風角度
|
||||
showBoatClassChartsLabel=利用可能な艇種の全体図も表示することができます。
|
||||
showDiagram=ダイアグラム表示
|
||||
rerunQueryAfterRefreshTooltip=テーブルがリフレッシュされた後にクエリを再実行します。
|
||||
@@ -1927,6 +1930,7 @@ finishingTime=フィニッシュ予定時刻
|
||||
finishTimeString=フィニッシュ時刻
|
||||
setFinishingAndFinishTime=フィニッシュ予定時刻とフィニッシュ時刻の設定
|
||||
finishTimeMustBeAtOrAfterFinishingTime=フィニッシュ時刻はフィニッシュ予定時刻以降であることが必要
|
||||
youHaveToEnterAFinishingTimeIfYouEnterAFinishedTime=フィニッシュ時刻を入力する場合、フィニッシュ予定時刻を入力することが必要
|
||||
boatNameMustNotBeEmpty=空でない艇名を指定してください
|
||||
errorUpdatingExpeditionDeviceConfiguration=Expedition デバイス設定 "{0}" の更新でエラーが発生: {1}
|
||||
doYouReallyWantToRemoveExpeditionDeviceConfiguration=Expedition デバイス設定 "{0}" を削除しますか
|
||||
@@ -2255,3 +2259,7 @@ editSwissTimingConnections=SwissTiming 接続の編集
|
||||
filterSwissTimingConnections=SwissTiming 接続のフィルタリング
|
||||
addSwissTimingConnection=SwissTiming 接続の追加
|
||||
updateURL=URL 更新
|
||||
startProcedure=スタート手順
|
||||
startMode=スタートモード
|
||||
gateOpeningTime=ゲートオープニング時刻
|
||||
protestTimeInMinutes=抗議受付時間 (分)
|
||||
+12
-4
@@ -807,7 +807,7 @@ explainNoConnectionsFromReplicas=Nenhuma conexão de uma réplica encontrada. Vo
|
||||
stopConnectionToMaster=Parar replicação
|
||||
asLink=Como link
|
||||
noStarttimeAnnouncedYet=Pendente
|
||||
raceIsRunning=Em execução
|
||||
raceIsRunning=Em competição
|
||||
raceIsFinishing=Concluindo
|
||||
raceIsFinished=Concluído
|
||||
raceIsScheduled=Planejado
|
||||
@@ -948,6 +948,7 @@ raceTimeUpwindTooltip=Tempo total navegado a barlavento nesta corrida
|
||||
noCustomGrouperScriptTextError=O script do sistema de agrupamento está vazio
|
||||
noGrouperSelectedError=Nenhum tipo de sistema de agrupamento selecionado
|
||||
dataMining=Data mining
|
||||
changeOrientation=Modificar orientação
|
||||
hideToolbar=Ocultar barra de ferramentas
|
||||
showSeriesLeaderboards=Visualizar painéis de classificação da série
|
||||
showOverallLeaderboard=Visualizar painel de classificação geral
|
||||
@@ -1051,8 +1052,10 @@ resultFromIgtimiWindImport=O número seguinte de pontos fixos foi importado para
|
||||
timeToStart={0} até partida
|
||||
timeSinceStart={0} desde partida
|
||||
distanceToLine=Distância para linha de partida
|
||||
TWATooltip=O ângulo entre a direção dos competidores e o vento
|
||||
TWATooltip=O ângulo entre o percurso no fundo dos competidores e o vento, assinalado, sendo os valores negativos vento de bombordo e os valores positivos vento de boreste
|
||||
absoluteTWATooltip=O ângulo positivo entre o percurso no fundo dos competidores e o vento, sem sinal positivo/negativo
|
||||
TWA=Ângulo do vento verdadeiro
|
||||
absoluteTWA=Ângulo do vento verdadeiro absoluto
|
||||
showBoatClassChartsLabel=Você também pode ver o diagrama geral para as classes de barcos disponíveis.
|
||||
showDiagram=Visualizar diagrama
|
||||
rerunQueryAfterRefreshTooltip=Executa novamente a consulta após a atualização das tabelas.
|
||||
@@ -1775,7 +1778,7 @@ couldNotSaveCompetitorRegistrations=Não foi possível gravar registros de compe
|
||||
searchTagForImportedCompetitorPlaceholder=Utilizar, por exemplo, corrida + nome da flotilha
|
||||
version=Versão: {0}
|
||||
replicables=Replicando objetos
|
||||
set=Definir
|
||||
set=Curso
|
||||
eventRegattaRacesLegendWind=# sensores de vento
|
||||
eventRegattaRacesLegendVideo=# fluxos de vídeo
|
||||
eventRegattaRacesLegendAudio=# fluxos de áudio
|
||||
@@ -1927,6 +1930,7 @@ finishingTime=Hora de chegada
|
||||
finishTimeString=Hora de conclusão
|
||||
setFinishingAndFinishTime=Definir hora de chegada e conclusão
|
||||
finishTimeMustBeAtOrAfterFinishingTime=A hora de conclusão deve ser igual ou posterior à hora de chegada
|
||||
youHaveToEnterAFinishingTimeIfYouEnterAFinishedTime=Você precisa inserir uma hora de chegada se inserir uma hora de conclusão
|
||||
boatNameMustNotBeEmpty=Forneça um nome de barco não vazio
|
||||
errorUpdatingExpeditionDeviceConfiguration=Erro ao atualizar configuração de dispositivo Expedition "{0}": {1}
|
||||
doYouReallyWantToRemoveExpeditionDeviceConfiguration=Você quer realmente remover a configuração do dispositivo Expedition "{0}"?
|
||||
@@ -2114,7 +2118,7 @@ gender=Sexo
|
||||
manage2SailEventIdBox=ID de evento Manage2Sail
|
||||
manage2SailEventURLBox=URL de evento Manage2Sail (json)
|
||||
manage2SailEventIdBoxTooltip=ID de evento ou um URL que contenha o ID de evento do Manage2Sail
|
||||
manage2SailPort=Porta
|
||||
manage2SailPort=Bombordo
|
||||
audioFiles=Faixas de áudio
|
||||
selectMedia=Selecionar mídia
|
||||
swissTimingUpdateURL=Atualizar URL (por exemplo, para hora de partida e feedback de mudança de percurso)
|
||||
@@ -2255,3 +2259,7 @@ editSwissTimingConnections=Processar conexão SwissTiming
|
||||
filterSwissTimingConnections=Filtrar conexões SwissTiming
|
||||
addSwissTimingConnection=Adicionar conexão SwissTiming
|
||||
updateURL=Atualizar URL
|
||||
startProcedure=Procedimento de partida
|
||||
startMode=Modo de partida
|
||||
gateOpeningTime=Hora de abertura do portão
|
||||
protestTimeInMinutes=Tempo de protesto em minutos
|
||||
+9
-1
@@ -948,6 +948,7 @@ raceTimeUpwindTooltip=Общее время хода против ветра в
|
||||
noCustomGrouperScriptTextError=Скрипт группирования пуст
|
||||
noGrouperSelectedError=Не выбран тип группирования
|
||||
dataMining=Добыча данных
|
||||
changeOrientation=Изменить ориентацию
|
||||
hideToolbar=Скрыть панель инструментов
|
||||
showSeriesLeaderboards=Показать таблицы лидеров серии
|
||||
showOverallLeaderboard=Показать итоговую таблицу лидеров
|
||||
@@ -1051,8 +1052,10 @@ resultFromIgtimiWindImport=Для перечисленных гонок импо
|
||||
timeToStart={0} до старта
|
||||
timeSinceStart={0} от старта
|
||||
distanceToLine=Дистанция до линии старта
|
||||
TWATooltip=Угол между направлением участников и ветром
|
||||
TWATooltip=Угол между курсом участников относительно грунта и ветром, со знаком, отрицательные значения означают ветер по левому борту, положительные - ветер по правому борту.
|
||||
absoluteTWATooltip=участников относительно грунта и ветром, без знака +/-
|
||||
TWA=Угол истинного ветра
|
||||
absoluteTWA=Абсолютный угол истинного ветра
|
||||
showBoatClassChartsLabel=Также можно просмотреть общую диаграмму для доступных классов лодок.
|
||||
showDiagram=Показать диаграмму
|
||||
rerunQueryAfterRefreshTooltip=Повторно выполняет запрос после обновления таблиц.
|
||||
@@ -1927,6 +1930,7 @@ finishingTime=Время финиширования
|
||||
finishTimeString=Время финиша
|
||||
setFinishingAndFinishTime=Указать время финиширования и финиша
|
||||
finishTimeMustBeAtOrAfterFinishingTime=Время финиша должно совпадать или быть после времени финиширования
|
||||
youHaveToEnterAFinishingTimeIfYouEnterAFinishedTime=При вводе времени финиша необходимо ввести время финиширования
|
||||
boatNameMustNotBeEmpty=Укажите непустое имя лодки
|
||||
errorUpdatingExpeditionDeviceConfiguration=Ошибка при обновлении конфигурации устройства Expedition "{0}": {1}
|
||||
doYouReallyWantToRemoveExpeditionDeviceConfiguration=Действительно удалить конфигурацию устройства Expedition "{0}"?
|
||||
@@ -2255,3 +2259,7 @@ editSwissTimingConnections=Редактировать соединение Swiss
|
||||
filterSwissTimingConnections=Фильтр соединений SwissTiming
|
||||
addSwissTimingConnection=Добавить соединение SwissTiming
|
||||
updateURL=Обновить URL
|
||||
startProcedure=Процедура старта
|
||||
startMode=Режим старта
|
||||
gateOpeningTime=Время открытия ворот
|
||||
protestTimeInMinutes=Времени протеста в минутах
|
||||
+11
-3
@@ -696,9 +696,9 @@ speedOverGroundFiveSecondsBeforeStartTooltip=起航前五秒时的实际速度
|
||||
speedOverGroundWhenPassingStartTooltip=参赛队越过起航线时的实际速度
|
||||
startTack=开始迎风转向
|
||||
startTackTooltip=参赛队开始迎风转向
|
||||
portTack=端口
|
||||
portTack=左舷
|
||||
starboardTack=右舷
|
||||
portSide=端口
|
||||
portSide=左舷
|
||||
starboardSide=右舷
|
||||
pleaseEnterANumber=请输入数字。
|
||||
competitorsFilter=参赛队筛选器
|
||||
@@ -948,6 +948,7 @@ raceTimeUpwindTooltip=本轮次比赛迎风航行的总时间
|
||||
noCustomGrouperScriptTextError=群集器脚本为空
|
||||
noGrouperSelectedError=未选择群集器类型
|
||||
dataMining=数据挖掘
|
||||
changeOrientation=更改方向
|
||||
hideToolbar=隐藏工具栏
|
||||
showSeriesLeaderboards=显示系列积分榜
|
||||
showOverallLeaderboard=显示总积分榜
|
||||
@@ -1051,8 +1052,10 @@ resultFromIgtimiWindImport=已为所列的比赛轮次导入以下修复: {0}
|
||||
timeToStart={0} - 开始
|
||||
timeSinceStart={0} - 开始后
|
||||
distanceToLine=与起航线的距离
|
||||
TWATooltip=赛队航行的方向和风的夹角
|
||||
TWATooltip=参赛队对地航向和风的夹角,设有符号标示,负值表示风来自左舷,正值表示风来自右舷
|
||||
absoluteTWATooltip=参赛队对地航向和风的正向夹角,不带 +/- 符号
|
||||
TWA=实际风向角
|
||||
absoluteTWA=绝对实际风向角
|
||||
showBoatClassChartsLabel=还可以查看可用船只级别的总直直方图。
|
||||
showDiagram=显示直方图
|
||||
rerunQueryAfterRefreshTooltip=在刷新表后,重新运行查询。
|
||||
@@ -1927,6 +1930,7 @@ finishingTime=结束时间
|
||||
finishTimeString=终点线时间
|
||||
setFinishingAndFinishTime=设置结束和终点线时间
|
||||
finishTimeMustBeAtOrAfterFinishingTime=终点线时间必须与结束时间相同或在其之后
|
||||
youHaveToEnterAFinishingTimeIfYouEnterAFinishedTime=如果输入终点线时间,则必须输入结束时间。
|
||||
boatNameMustNotBeEmpty=请提供非空船只名称
|
||||
errorUpdatingExpeditionDeviceConfiguration=更新导航图设备配置 "{0}" 出错:{1}
|
||||
doYouReallyWantToRemoveExpeditionDeviceConfiguration=是否确定移除导航图设备配置 "{0}"?
|
||||
@@ -2255,3 +2259,7 @@ editSwissTimingConnections=编辑 SwissTiming 连接
|
||||
filterSwissTimingConnections=筛选 SwissTiming 连接
|
||||
addSwissTimingConnection=添加 SwissTiming 连接
|
||||
updateURL=更新 URL
|
||||
startProcedure=起航程序
|
||||
startMode=起航模式
|
||||
gateOpeningTime=门标开启时间
|
||||
protestTimeInMinutes=声明时间(分钟)
|
||||
+4
-10
@@ -5209,19 +5209,13 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
|
||||
|
||||
@Override
|
||||
public List<String> getResultImportUrls(String resultProviderName) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
final List<String> result = new ArrayList<String>();
|
||||
ResultUrlProvider urlBasedScoreCorrectionProvider = getUrlBasedScoreCorrectionProvider(resultProviderName);
|
||||
ResultUrlRegistry resultUrlRegistry = getResultUrlRegistry();
|
||||
if (urlBasedScoreCorrectionProvider != null) {
|
||||
Iterable<URL> allUrls = resultUrlRegistry.getResultUrls(resultProviderName);
|
||||
final Subject subject = SecurityUtils.getSubject();
|
||||
for (URL url : allUrls) {
|
||||
if (subject
|
||||
.isPermitted(SecuredDomainType.RESULT_IMPORT_URL.getStringPermissionForTypeRelativeIdentifier(
|
||||
DefaultActions.READ, new TypeRelativeObjectIdentifier(
|
||||
urlBasedScoreCorrectionProvider.getName(), url.toString())))) {
|
||||
result.add(url.toString());
|
||||
}
|
||||
Iterable<URL> allUrlsReadableBySubject = resultUrlRegistry.getResultUrls(resultProviderName);
|
||||
for (URL url : allUrlsReadableBySubject) {
|
||||
result.add(url.toString());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -12,8 +12,12 @@ Export-Package: com.sap.sailing.resultimport,
|
||||
com.sap.sailing.resultimport.impl
|
||||
Import-Package: com.sap.sailing.competitorimport,
|
||||
com.sap.sailing.domain.common,
|
||||
com.sap.sailing.domain.common.security,
|
||||
com.sap.sailing.domain.persistence,
|
||||
com.sap.sailing.server.interfaces,
|
||||
com.sap.sse.security.shared,
|
||||
org.apache.shiro;version="1.2.2",
|
||||
org.apache.shiro.subject;version="1.2.2",
|
||||
org.osgi.framework;version="1.5.0",
|
||||
org.osgi.util.tracker;version="1.5.1"
|
||||
Require-Bundle: com.sap.sse.common
|
||||
+29
@@ -2,14 +2,43 @@ package com.sap.sailing.resultimport;
|
||||
|
||||
import java.net.URL;
|
||||
|
||||
import org.apache.shiro.subject.Subject;
|
||||
|
||||
import com.sap.sailing.domain.common.security.SecuredDomainType;
|
||||
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
|
||||
|
||||
/**
|
||||
* A registry for result URL's of score correction providers
|
||||
* @author Frank
|
||||
*/
|
||||
public interface ResultUrlRegistry {
|
||||
/**
|
||||
* Registers the URL {@code url} for the result provider named {@code resultProviderName}. Unlike the
|
||||
* {@link #getResultUrls(String)} method, here no security check is performed. The caller is expected to assert that
|
||||
* the calling {@link Subject} has the permission {@link SecuredDomainType#RESULT_IMPORT_URL
|
||||
* RESULT_IMPORT_URL}.{@link DefaultActions#CREATE CREATE} after setting the ownership. Implementing this
|
||||
* check properly is done best with the {@code SecurityService} which is not visible from this bundle.
|
||||
*/
|
||||
void registerResultUrl(String resultProviderName, URL url);
|
||||
|
||||
/**
|
||||
* Registers the URL {@code url} from the result provider named {@code resultProviderName}. Unlike the
|
||||
* {@link #getResultUrls(String)} method, here no security check is performed. The caller is expected to assert that
|
||||
* the calling {@link Subject} has the permission {@link SecuredDomainType#RESULT_IMPORT_URL
|
||||
* RESULT_IMPORT_URL}.{@link DefaultActions#DELETE DELETE} after setting the ownership. Implementing this
|
||||
* check properly is done best with the {@code SecurityService} which is not visible from this bundle.
|
||||
*/
|
||||
void unregisterResultUrl(String resultProviderName, URL url);
|
||||
|
||||
/**
|
||||
* Obtains the result import URLs registered for the provider named {@code resultProviderName}. If no such URLs are
|
||||
* found, an empty iterable is returned. The URLs returned are filtered by the calling {@link Subject}'s permission
|
||||
* to The URLs returned are filtered by the calling {@link Subject}'s permission
|
||||
* to {@link SecuredDomainType#RESULT_IMPORT_URL RESULT_IMPORT_URL}.{@link DefaultActions#READ READ} them. them.
|
||||
*
|
||||
* @return an always valid, non-{@code null} iterable
|
||||
*/
|
||||
Iterable<URL> getResultUrls(String resultProviderName);
|
||||
|
||||
Iterable<String> getResultProviderNames();
|
||||
}
|
||||
+20
-3
@@ -1,14 +1,22 @@
|
||||
package com.sap.sailing.resultimport.impl;
|
||||
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.shiro.SecurityUtils;
|
||||
import org.apache.shiro.subject.Subject;
|
||||
|
||||
import com.sap.sailing.domain.common.security.SecuredDomainType;
|
||||
import com.sap.sailing.domain.persistence.DomainObjectFactory;
|
||||
import com.sap.sailing.domain.persistence.MongoObjectFactory;
|
||||
import com.sap.sailing.resultimport.ResultUrlRegistry;
|
||||
import com.sap.sse.security.shared.HasPermissions.DefaultActions;
|
||||
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
|
||||
|
||||
public class ResultUrlRegistryImpl implements ResultUrlRegistry {
|
||||
private final Map<String, Set<URL>> resultUrls;
|
||||
@@ -41,9 +49,18 @@ public class ResultUrlRegistryImpl implements ResultUrlRegistry {
|
||||
|
||||
@Override
|
||||
public Iterable<URL> getResultUrls(String resultProviderName) {
|
||||
Iterable<URL> result;
|
||||
if(resultUrls.containsKey(resultProviderName)) {
|
||||
result = resultUrls.get(resultProviderName);
|
||||
final Subject subject = SecurityUtils.getSubject();
|
||||
final Iterable<URL> result;
|
||||
if (resultUrls.containsKey(resultProviderName)) {
|
||||
result = new ArrayList<>(resultUrls.get(resultProviderName));
|
||||
for (final Iterator<URL> urlIterator = result.iterator(); urlIterator.hasNext();) {
|
||||
final URL url = urlIterator.next();
|
||||
if (!subject.isPermitted(SecuredDomainType.RESULT_IMPORT_URL
|
||||
.getStringPermissionForTypeRelativeIdentifier(DefaultActions.READ,
|
||||
new TypeRelativeObjectIdentifier(resultProviderName, url.toString())))) {
|
||||
urlIterator.remove();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = Collections.emptySet();
|
||||
}
|
||||
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
package com.sap.sailing.selenium.api.event;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.selenium.api.core.ApiContext;
|
||||
import com.sap.sailing.selenium.api.core.JsonWrapper;
|
||||
import com.sap.sse.common.Util.Triple;
|
||||
|
||||
public class TrackedEventsApi {
|
||||
|
||||
private static final String TRACKED_EVENTS_URL = "/api/v1/trackedevents/";
|
||||
private static final String KEY_TRACKED_EVENTS = "trackedEvents";
|
||||
|
||||
private static final String KEY_LEADERBOARD_NAME = "leaderboardName";
|
||||
private static final String KEY_EVENT_ID = "eventId";
|
||||
private static final String KEY_EVENT_IS_ARCHIVED = "isArchived";
|
||||
private static final String KEY_EVENT_NAME = "name";
|
||||
private static final String KEY_EVENT_START = "start";
|
||||
private static final String KEY_EVENT_END = "end";
|
||||
private static final String KEY_EVENT_BASE_URL = "url";
|
||||
private static final String KEY_EVENT_IS_OWNER = "isOwner";
|
||||
private static final String KEY_EVENT_REGATTA_SECRET = "regattaSecret";
|
||||
|
||||
private static final String KEY_EVENT_TRACKED_ELEMENTS = "trackedElements";
|
||||
|
||||
private static final String KEY_TRACKED_ELEMENT_DEVICE_ID = "deviceId";
|
||||
private static final String KEY_TRACKED_ELEMENT_COMPETITOR_ID = "competitorId";
|
||||
private static final String KEY_TRACKED_ELEMENT_BOAT_ID = "boatId";
|
||||
private static final String KEY_TRACKED_ELEMENT_MARK_ID = "markId";
|
||||
|
||||
private static final String KEY_QUERY_INCLUDE_ARCHIVED = "includeArchived";
|
||||
|
||||
public TrackedEvents getTrackedEvents(ApiContext ctx, boolean isArchived) {
|
||||
final Map<String, String> queryParams = new HashMap<>();
|
||||
queryParams.put(KEY_QUERY_INCLUDE_ARCHIVED, Boolean.toString(isArchived));
|
||||
return new TrackedEvents(ctx.get(TRACKED_EVENTS_URL, queryParams));
|
||||
}
|
||||
|
||||
public void updateOrCreateTrackedEvent(ApiContext ctx, String eventId, String leaderboardName, String eventBaseUrl,
|
||||
String deviceId, Set<Triple<String, String, String>> trackedIds, String regattaSecret) {
|
||||
final JSONObject json = new JSONObject();
|
||||
json.put(KEY_EVENT_BASE_URL, eventBaseUrl);
|
||||
|
||||
final JSONArray elements = new JSONArray();
|
||||
for (Triple<String, String, String> trackedId : trackedIds) {
|
||||
final JSONObject elem = new JSONObject();
|
||||
elem.put(KEY_TRACKED_ELEMENT_DEVICE_ID, deviceId);
|
||||
if (trackedId.getA() != null) {
|
||||
elem.put(KEY_TRACKED_ELEMENT_COMPETITOR_ID, trackedId.getA());
|
||||
} else if (trackedId.getB() != null) {
|
||||
elem.put(KEY_TRACKED_ELEMENT_BOAT_ID, trackedId.getB());
|
||||
} else if (trackedId.getC() != null) {
|
||||
elem.put(KEY_TRACKED_ELEMENT_MARK_ID, trackedId.getC());
|
||||
}
|
||||
elements.add(elem);
|
||||
}
|
||||
|
||||
json.put(KEY_EVENT_REGATTA_SECRET, regattaSecret);
|
||||
json.put(KEY_EVENT_TRACKED_ELEMENTS, elements);
|
||||
json.put(KEY_EVENT_IS_ARCHIVED, false);
|
||||
ctx.put(TRACKED_EVENTS_URL + eventId + "/" + leaderboardName, new HashMap<>(), json);
|
||||
}
|
||||
|
||||
public void setArchived(ApiContext ctx, String eventId, String leaderboardName, boolean archived) {
|
||||
final HashMap<String, String> formParams = new HashMap<>();
|
||||
formParams.put(KEY_QUERY_INCLUDE_ARCHIVED, Boolean.toString(archived));
|
||||
ctx.post(TRACKED_EVENTS_URL + eventId + "/" + leaderboardName, new HashMap<String, String>(), formParams);
|
||||
}
|
||||
|
||||
public void deleteEventTrackings(ApiContext ctx, String eventId, String leaderboardName) {
|
||||
ctx.delete(TRACKED_EVENTS_URL + eventId + "/" + leaderboardName);
|
||||
}
|
||||
|
||||
public class TrackedElement extends JsonWrapper {
|
||||
public TrackedElement(JSONObject json) {
|
||||
super(json);
|
||||
}
|
||||
|
||||
public String getCompetitorId() {
|
||||
return get(KEY_TRACKED_ELEMENT_COMPETITOR_ID);
|
||||
}
|
||||
|
||||
public String getBoatId() {
|
||||
return get(KEY_TRACKED_ELEMENT_BOAT_ID);
|
||||
}
|
||||
|
||||
public String getMarkId() {
|
||||
return get(KEY_TRACKED_ELEMENT_MARK_ID);
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return get(KEY_TRACKED_ELEMENT_DEVICE_ID);
|
||||
}
|
||||
}
|
||||
|
||||
public class TrackedEvents extends JsonWrapper {
|
||||
public TrackedEvents(JSONObject json) {
|
||||
super(json);
|
||||
}
|
||||
|
||||
public Iterable<TrackedEvent> getEvents() {
|
||||
final Collection<TrackedEvent> events = new ArrayList<>();
|
||||
final JSONArray array = get(KEY_TRACKED_EVENTS);
|
||||
for (final Object json : array) {
|
||||
events.add(new TrackedEvent((JSONObject) json));
|
||||
}
|
||||
return events;
|
||||
}
|
||||
}
|
||||
|
||||
public class TrackedEvent extends JsonWrapper {
|
||||
|
||||
public TrackedEvent(JSONObject json) {
|
||||
super(json);
|
||||
}
|
||||
|
||||
public String getEventId() {
|
||||
return get(KEY_EVENT_ID);
|
||||
}
|
||||
|
||||
public String getEventName() {
|
||||
return get(KEY_EVENT_NAME);
|
||||
}
|
||||
|
||||
public String getEventStart() {
|
||||
return get(KEY_EVENT_START);
|
||||
}
|
||||
|
||||
public String getEventEnd() {
|
||||
return get(KEY_EVENT_END);
|
||||
}
|
||||
|
||||
public String getEventBaseUrl() {
|
||||
return get(KEY_EVENT_BASE_URL);
|
||||
}
|
||||
|
||||
public boolean isArchived() {
|
||||
return get(KEY_EVENT_IS_ARCHIVED);
|
||||
}
|
||||
|
||||
public boolean isOwner() {
|
||||
return get(KEY_EVENT_IS_OWNER);
|
||||
}
|
||||
|
||||
public Iterable<TrackedElement> getTrackedElements() {
|
||||
final JSONArray array = get(KEY_EVENT_TRACKED_ELEMENTS);
|
||||
final Collection<TrackedElement> col = new ArrayList<>();
|
||||
if (array != null) {
|
||||
for (final Object w : array) {
|
||||
col.add(new TrackedElement((JSONObject) w));
|
||||
}
|
||||
}
|
||||
return col;
|
||||
}
|
||||
|
||||
public String getLeaderboardName() {
|
||||
return get(KEY_LEADERBOARD_NAME);
|
||||
}
|
||||
|
||||
public String getRegattaSecret() {
|
||||
return get(KEY_EVENT_REGATTA_SECRET);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+10
-1
@@ -14,7 +14,9 @@ public class RegattaApi {
|
||||
|
||||
private static final String REGATTAS = "/api/v1/regattas";
|
||||
private static final String LIST_REGATTA_RACES = "/races";
|
||||
private static final String COMPETITOR_CREATE_AND_ADD_WITH_BOAT = "/competitors/createandadd";
|
||||
private static final String COMPETITORS = "/competitors";
|
||||
private static final String COMPETITOR_CREATE_AND_ADD_WITH_BOAT = COMPETITORS + "/createandadd";
|
||||
private static final String ADD = "/add";
|
||||
private static final String ADD_RACE_COLUMN_URL = "/addracecolumns";
|
||||
private static final String LIST_COMPETITORS = "/competitors";
|
||||
|
||||
@@ -87,5 +89,12 @@ public class RegattaApi {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void addCompetitor(ApiContext ctx, String regattaName, UUID competitorId, Optional<String> secret) {
|
||||
final String url = REGATTAS + "/" + regattaName + COMPETITORS + "/" + competitorId + ADD;
|
||||
final Map<String, String> queryParams = new TreeMap<>();
|
||||
queryParams.put("secret", secret.orElse(null));
|
||||
ctx.post(url, queryParams);
|
||||
}
|
||||
|
||||
}
|
||||
+94
-2
@@ -3,39 +3,54 @@ package com.sap.sailing.selenium.api.test;
|
||||
import static com.sap.sailing.domain.common.CompetitorRegistrationType.CLOSED;
|
||||
import static com.sap.sailing.selenium.api.core.ApiContext.SERVER_CONTEXT;
|
||||
import static com.sap.sailing.selenium.api.core.ApiContext.createAdminApiContext;
|
||||
import static com.sap.sailing.selenium.api.core.ApiContext.createApiContext;
|
||||
import static com.sap.sailing.selenium.pages.adminconsole.AdminConsolePage.goToPage;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.common.CompetitorRegistrationType;
|
||||
import com.sap.sailing.selenium.api.core.ApiContext;
|
||||
import com.sap.sailing.selenium.api.event.EventApi;
|
||||
import com.sap.sailing.selenium.api.event.EventApi.Event;
|
||||
import com.sap.sailing.selenium.api.event.LeaderboardApi;
|
||||
import com.sap.sailing.selenium.api.event.SecurityApi;
|
||||
import com.sap.sailing.selenium.api.event.UserGroupApi;
|
||||
import com.sap.sailing.selenium.api.event.UserGroupApi.UserGroup;
|
||||
import com.sap.sailing.selenium.api.regatta.Competitor;
|
||||
import com.sap.sailing.selenium.api.regatta.RaceColumn;
|
||||
import com.sap.sailing.selenium.api.regatta.Regatta;
|
||||
import com.sap.sailing.selenium.api.regatta.RegattaApi;
|
||||
import com.sap.sailing.selenium.api.regatta.RegattaRaces;
|
||||
import com.sap.sailing.selenium.pages.adminconsole.AdminConsolePage;
|
||||
import com.sap.sailing.selenium.test.AbstractSeleniumTest;
|
||||
|
||||
public class RegattaApiTest extends AbstractSeleniumTest {
|
||||
|
||||
private static String EVENT_NAME = "<ppp> loggingsession";
|
||||
private static String OTHER_EVENT_NAME = "<xxx> loggingsession";
|
||||
private static String BOAT_CLASS = "75QMNATIONALEKREUZER";
|
||||
|
||||
private final EventApi eventApi = new EventApi();
|
||||
private final RegattaApi regattaApi = new RegattaApi();
|
||||
private final LeaderboardApi leaderboardApi = new LeaderboardApi();
|
||||
private final UserGroupApi usergroupApi = new UserGroupApi();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
clearState(getContextRoot(), /* headless */ true);
|
||||
super.setUp();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -73,8 +88,8 @@ public class RegattaApiTest extends AbstractSeleniumTest {
|
||||
RegattaRaces regattaRaces = regattaApi.getRegattaRaces(ctx, EVENT_NAME);
|
||||
|
||||
assertEquals("read: regatta is different", EVENT_NAME, regattaRaces.getRegattaName());
|
||||
// assertEquals("read: reagtta.series should have 0 entries", 0, regattaRaces.getRaces().length);
|
||||
|
||||
//assertEquals("read: reagtta.series should have 0 entries", 0, regattaRaces.getRaces().length);
|
||||
|
||||
RaceColumn[] raceColumns = regattaApi.addRaceColumn(ctx, EVENT_NAME, "R", 1);
|
||||
leaderboardApi.startRaceLogTracking(ctx, EVENT_NAME, raceColumns[0].getRaceName(), "Default");
|
||||
regattaRaces = regattaApi.getRegattaRaces(ctx, EVENT_NAME);
|
||||
@@ -136,4 +151,81 @@ public class RegattaApiTest extends AbstractSeleniumTest {
|
||||
// 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");
|
||||
final ApiContext userWithCompetitorCtx = createApiContext(getContextRoot(), SERVER_CONTEXT, "donald", "daisy0815");
|
||||
final ApiContext userWithCompetitorAndSecurityCtx = createApiContext(getContextRoot(),
|
||||
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-tenant");
|
||||
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());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRegisterExistingCompetitorWithBadSecret() {
|
||||
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");
|
||||
final ApiContext userWithCompetitorCtx = createApiContext(getContextRoot(), SERVER_CONTEXT, "donald",
|
||||
"daisy0815");
|
||||
securityApi.createUser(adminSecurityCtx, "dagobert", "Dagobert Duck", null, "daisy1337");
|
||||
final ApiContext userOwningEventCtx = createApiContext(getContextRoot(), SERVER_CONTEXT, "dagobert",
|
||||
"daisy1337");
|
||||
|
||||
final ApiContext userWithCompetitorAndSecurityCtx = createApiContext(getContextRoot(),
|
||||
ApiContext.SECURITY_CONTEXT, "donald", "daisy0815");
|
||||
|
||||
// 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-tenant");
|
||||
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");
|
||||
try {
|
||||
regattaApi.addCompetitor(userWithCompetitorCtx, OTHER_EVENT_NAME, competitor.getId(),
|
||||
Optional.of(eventToRegisterExistingCompetitor.getSecret() + "bad"));
|
||||
Assert.fail("Expected error because of bad secret.");
|
||||
} catch (RuntimeException e) {
|
||||
Assert.assertTrue("Exepcted HTTP 401 - Unauthorized", e.getMessage().contains("rc=401"));
|
||||
}
|
||||
|
||||
Competitor[] competitors = regattaApi.getCompetitors(userOwningEventCtx, OTHER_EVENT_NAME);
|
||||
|
||||
assertEquals(0, competitors.length);
|
||||
}
|
||||
}
|
||||
+332
@@ -0,0 +1,332 @@
|
||||
package com.sap.sailing.selenium.api.test;
|
||||
|
||||
import static com.sap.sailing.selenium.api.core.ApiContext.SERVER_CONTEXT;
|
||||
import static com.sap.sailing.selenium.api.core.ApiContext.createAdminApiContext;
|
||||
import static com.sap.sailing.selenium.api.core.ApiContext.createApiContext;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.common.CompetitorRegistrationType;
|
||||
import com.sap.sailing.selenium.api.core.ApiContext;
|
||||
import com.sap.sailing.selenium.api.event.EventApi;
|
||||
import com.sap.sailing.selenium.api.event.EventApi.Event;
|
||||
import com.sap.sailing.selenium.api.event.SecurityApi;
|
||||
import com.sap.sailing.selenium.api.event.TrackedEventsApi;
|
||||
import com.sap.sailing.selenium.api.event.TrackedEventsApi.TrackedElement;
|
||||
import com.sap.sailing.selenium.api.event.TrackedEventsApi.TrackedEvent;
|
||||
import com.sap.sailing.selenium.api.event.TrackedEventsApi.TrackedEvents;
|
||||
import com.sap.sailing.selenium.test.AbstractSeleniumTest;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Triple;
|
||||
|
||||
public class TrackedEventsResourceApiTest extends AbstractSeleniumTest {
|
||||
|
||||
private final TrackedEventsApi trackedEventsApi = new TrackedEventsApi();
|
||||
|
||||
private final EventApi eventApi = new EventApi();
|
||||
private final SecurityApi securityApi = new SecurityApi();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
clearState(getContextRoot());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetTrackedEventsEmpty() {
|
||||
final ApiContext adminCtx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT);
|
||||
final TrackedEvents trackedEvents = trackedEventsApi.getTrackedEvents(adminCtx, true);
|
||||
Assert.assertNotNull("Tracked Events element should not be null", trackedEvents);
|
||||
Assert.assertTrue("Expected empty list of tracked events", Util.isEmpty(trackedEvents.getEvents()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateMultipleTrackingEventsAndEditOne() {
|
||||
final ApiContext adminCtx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT);
|
||||
final String competitorId = UUID.randomUUID().toString();
|
||||
final String boatId = UUID.randomUUID().toString();
|
||||
final String markId = UUID.randomUUID().toString();
|
||||
|
||||
final String eventBaseUrl = "testUrl";
|
||||
final String deviceId = UUID.randomUUID().toString();
|
||||
|
||||
final Event evt = eventApi.createEvent(adminCtx, "TestEvent-" + UUID.randomUUID().toString(),
|
||||
"75QMNATIONALEKREUZER", CompetitorRegistrationType.CLOSED, "Mannheim");
|
||||
final String eventId = evt.getId();
|
||||
final String leaderboardName = evt.getName();
|
||||
final String regattaSecret = evt.getSecret();
|
||||
|
||||
final Event evt2 = eventApi.createEvent(adminCtx, "TestEvent-" + UUID.randomUUID().toString(),
|
||||
"75QMNATIONALEKREUZER", CompetitorRegistrationType.CLOSED, "Mannheim");
|
||||
final String eventId2 = evt2.getId();
|
||||
final String leaderboardName2 = evt2.getName();
|
||||
final String regattaSecret2 = evt.getSecret();
|
||||
|
||||
final Set<Triple<String, String, String>> trackedIds = new HashSet<>();
|
||||
trackedIds.add(new Triple<>(competitorId, null, null));
|
||||
trackedIds.add(new Triple<>(null, boatId, null));
|
||||
trackedEventsApi.updateOrCreateTrackedEvent(adminCtx, eventId, leaderboardName, eventBaseUrl, deviceId,
|
||||
trackedIds, regattaSecret);
|
||||
trackedEventsApi.updateOrCreateTrackedEvent(adminCtx, eventId2, leaderboardName2, eventBaseUrl, deviceId,
|
||||
new HashSet<>(Arrays.asList(new Triple<>(null, null, markId))), regattaSecret2);
|
||||
|
||||
trackedEventsApi.setArchived(adminCtx, evt.getId(), leaderboardName, true);
|
||||
trackedEventsApi.setArchived(adminCtx, evt2.getId(), leaderboardName2, false);
|
||||
// check if created event is still there
|
||||
final TrackedEvents trackedEvents = trackedEventsApi.getTrackedEvents(adminCtx, false);
|
||||
|
||||
int cntEvents = 0;
|
||||
for (final TrackedEvent event : trackedEvents.getEvents()) {
|
||||
cntEvents++;
|
||||
Assert.assertEquals("Unexpected event base url", eventBaseUrl, event.getEventBaseUrl());
|
||||
if (eventId.equals(event.getEventId())) {
|
||||
Assert.assertEquals("Unexpected event ID", eventId, event.getEventId());
|
||||
Assert.assertEquals("Unexpected leaderboard name", leaderboardName, event.getLeaderboardName());
|
||||
Assert.assertEquals("Unexpected regatta secret", regattaSecret, event.getRegattaSecret());
|
||||
Assert.fail("Expected event 1 to not be shown anymore.");
|
||||
} else if (eventId2.equals(event.getEventId())) {
|
||||
Assert.assertEquals("Unexpected event ID", eventId2, event.getEventId());
|
||||
Assert.assertEquals("Unexpected leaderboard name", leaderboardName2, event.getLeaderboardName());
|
||||
Assert.assertEquals("Unexpected regatta secret", regattaSecret2, event.getRegattaSecret());
|
||||
} else {
|
||||
Assert.fail("Invalid event id.");
|
||||
}
|
||||
|
||||
int cntElements = 0;
|
||||
for (final TrackedElement elem : event.getTrackedElements()) {
|
||||
cntElements++;
|
||||
final boolean correctBoatId = boatId.equals(elem.getBoatId());
|
||||
final boolean correctCompetitorId = competitorId.equals(elem.getCompetitorId());
|
||||
final boolean correctMarkId = markId.equals(elem.getMarkId());
|
||||
|
||||
Assert.assertEquals("Unexpected device ID", deviceId, elem.getDeviceId());
|
||||
Assert.assertTrue("More than one or zero items tracked in this element",
|
||||
correctBoatId ^ correctCompetitorId ^ correctMarkId);
|
||||
}
|
||||
Assert.assertEquals("Invalid numer of elements in this event", 1, cntElements);
|
||||
}
|
||||
|
||||
Assert.assertEquals("Expected 1 events", 1, cntEvents);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateMultipleTrackingEvents() {
|
||||
final ApiContext adminCtx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT);
|
||||
final String competitorId = UUID.randomUUID().toString();
|
||||
final String boatId = UUID.randomUUID().toString();
|
||||
final String markId = UUID.randomUUID().toString();
|
||||
|
||||
final String eventBaseUrl = "testUrl";
|
||||
final String deviceId = UUID.randomUUID().toString();
|
||||
|
||||
final Event evt = eventApi.createEvent(adminCtx, "TestEvent-" + UUID.randomUUID().toString(),
|
||||
"75QMNATIONALEKREUZER", CompetitorRegistrationType.CLOSED, "Mannheim");
|
||||
final String eventId = evt.getId();
|
||||
final String leaderboardName = evt.getName();
|
||||
final String regattaSecret = evt.getSecret();
|
||||
|
||||
final Event evt2 = eventApi.createEvent(adminCtx, "TestEvent-" + UUID.randomUUID().toString(),
|
||||
"75QMNATIONALEKREUZER", CompetitorRegistrationType.CLOSED, "Mannheim");
|
||||
final String eventId2 = evt2.getId();
|
||||
final String leaderboardName2 = evt2.getName();
|
||||
final String regattaSecret2 = evt2.getSecret();
|
||||
|
||||
final Set<Triple<String, String, String>> trackedIds = new HashSet<>();
|
||||
trackedIds.add(new Triple<>(competitorId, null, null));
|
||||
trackedIds.add(new Triple<>(null, boatId, null));
|
||||
trackedEventsApi.updateOrCreateTrackedEvent(adminCtx, eventId, leaderboardName, eventBaseUrl, deviceId,
|
||||
trackedIds, regattaSecret);
|
||||
trackedEventsApi.updateOrCreateTrackedEvent(adminCtx, eventId2, leaderboardName2, eventBaseUrl, deviceId,
|
||||
new HashSet<>(Arrays.asList(new Triple<>(null, null, markId))), regattaSecret2);
|
||||
|
||||
// check if created event is still there
|
||||
final TrackedEvents trackedEvents = trackedEventsApi.getTrackedEvents(adminCtx, true);
|
||||
|
||||
int cntEvents = 0;
|
||||
for (final TrackedEvent event : trackedEvents.getEvents()) {
|
||||
cntEvents++;
|
||||
Assert.assertEquals("Unexpected event base url", eventBaseUrl, event.getEventBaseUrl());
|
||||
if (eventId.equals(event.getEventId())) {
|
||||
Assert.assertEquals("Unexpected event ID", eventId, event.getEventId());
|
||||
Assert.assertEquals("Unexpected leaderboard name", leaderboardName, event.getLeaderboardName());
|
||||
Assert.assertEquals("Unexpected regatta secret", regattaSecret, event.getRegattaSecret());
|
||||
} else if (eventId2.equals(event.getEventId())) {
|
||||
Assert.assertEquals("Unexpected event ID", eventId2, event.getEventId());
|
||||
Assert.assertEquals("Unexpected leaderboard name", leaderboardName2, event.getLeaderboardName());
|
||||
Assert.assertEquals("Unexpected regatta secret", regattaSecret2, event.getRegattaSecret());
|
||||
} else {
|
||||
Assert.fail("Invalid event id.");
|
||||
}
|
||||
|
||||
int cntElements = 0;
|
||||
for (final TrackedElement elem : event.getTrackedElements()) {
|
||||
cntElements++;
|
||||
final boolean correctBoatId = boatId.equals(elem.getBoatId());
|
||||
final boolean correctCompetitorId = competitorId.equals(elem.getCompetitorId());
|
||||
final boolean correctMarkId = markId.equals(elem.getMarkId());
|
||||
|
||||
Assert.assertEquals("Unexpected device ID", deviceId, elem.getDeviceId());
|
||||
Assert.assertTrue("More than one or zero items tracked in this element",
|
||||
correctBoatId ^ correctCompetitorId ^ correctMarkId);
|
||||
}
|
||||
Assert.assertEquals("Invalid numer of elements in this event", eventId.equals(event.getEventId()) ? 2 : 1,
|
||||
cntElements);
|
||||
}
|
||||
|
||||
Assert.assertEquals("Expected 2 events", 2, cntEvents);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateMultipleTrackingsForSameEvent() {
|
||||
final ApiContext adminCtx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT);
|
||||
final String eventName = "TestEvent-" + UUID.randomUUID().toString();
|
||||
final String competitorId = UUID.randomUUID().toString();
|
||||
final String boatId = UUID.randomUUID().toString();
|
||||
final String markId = UUID.randomUUID().toString();
|
||||
|
||||
final String eventBaseUrl = "testUrl";
|
||||
final String deviceId = UUID.randomUUID().toString();
|
||||
|
||||
final Event evt = eventApi.createEvent(adminCtx, eventName, "75QMNATIONALEKREUZER",
|
||||
CompetitorRegistrationType.CLOSED, "Mannheim");
|
||||
final String eventId = evt.getId();
|
||||
final String leaderboardName = evt.getName();
|
||||
final String regattaSecret = evt.getSecret();
|
||||
|
||||
final Set<Triple<String, String, String>> trackedIds = new HashSet<>();
|
||||
trackedIds.add(new Triple<>(competitorId, null, null));
|
||||
trackedIds.add(new Triple<>(null, boatId, null));
|
||||
trackedIds.add(new Triple<>(null, null, markId));
|
||||
trackedEventsApi.updateOrCreateTrackedEvent(adminCtx, eventId, leaderboardName, eventBaseUrl, deviceId,
|
||||
trackedIds, regattaSecret);
|
||||
|
||||
// check if created event is still there
|
||||
final TrackedEvents trackedEvents = trackedEventsApi.getTrackedEvents(adminCtx, true);
|
||||
|
||||
int cntEvents = 0;
|
||||
for (final TrackedEvent event : trackedEvents.getEvents()) {
|
||||
cntEvents++;
|
||||
Assert.assertEquals("Unexpected event base url", eventBaseUrl, event.getEventBaseUrl());
|
||||
Assert.assertEquals("Unexpected event ID", eventId, event.getEventId());
|
||||
Assert.assertEquals("Unexpected leaderboard name", leaderboardName, event.getLeaderboardName());
|
||||
Assert.assertEquals("Unexpected regatta secret", regattaSecret, event.getRegattaSecret());
|
||||
|
||||
int cntElements = 0;
|
||||
for (final TrackedElement elem : event.getTrackedElements()) {
|
||||
cntElements++;
|
||||
final boolean correctBoatId = boatId.equals(elem.getBoatId());
|
||||
final boolean correctCompetitorId = competitorId.equals(elem.getCompetitorId());
|
||||
final boolean correctMarkId = markId.equals(elem.getMarkId());
|
||||
|
||||
Assert.assertEquals("Unexpected device ID", deviceId, elem.getDeviceId());
|
||||
Assert.assertTrue("More than one or zero items tracked in this element",
|
||||
correctBoatId ^ correctCompetitorId ^ correctMarkId);
|
||||
}
|
||||
Assert.assertEquals("Expected 3 event elements", 3, cntElements);
|
||||
}
|
||||
|
||||
Assert.assertEquals("Expected exactly 1 event", 1, cntEvents);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTrackedEventWithCompetitorTracking() {
|
||||
final ApiContext adminCtx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT);
|
||||
testCreateTrackedEventWithTracking(UUID.randomUUID().toString(), null, null, adminCtx,
|
||||
"TestEvent-" + UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTrackedEventWithBoatTracking() {
|
||||
final ApiContext adminCtx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT);
|
||||
testCreateTrackedEventWithTracking(null, UUID.randomUUID().toString(), null, adminCtx,
|
||||
"TestEvent-" + UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateTrackedEventWithMarkTracking() {
|
||||
final ApiContext adminCtx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT);
|
||||
testCreateTrackedEventWithTracking(null, null, UUID.randomUUID().toString(), adminCtx,
|
||||
"TestEvent-" + UUID.randomUUID().toString());
|
||||
}
|
||||
|
||||
private String testCreateTrackedEventWithTracking(final String competitorId, final String boatId,
|
||||
final String markId, final ApiContext adminCtx, final String eventName) {
|
||||
final String eventBaseUrl = "testUrl";
|
||||
final String deviceId = UUID.randomUUID().toString();
|
||||
|
||||
final Event evt = eventApi.createEvent(adminCtx, eventName, "75QMNATIONALEKREUZER",
|
||||
CompetitorRegistrationType.CLOSED, "Mannheim");
|
||||
final String eventId = evt.getId();
|
||||
final String leaderboardName = evt.getName();
|
||||
final String regattaSecret = evt.getSecret();
|
||||
|
||||
trackedEventsApi.updateOrCreateTrackedEvent(adminCtx, eventId, leaderboardName, eventBaseUrl, deviceId,
|
||||
new HashSet<>(Arrays.asList(new Triple<>(competitorId, boatId, markId))), regattaSecret);
|
||||
|
||||
// check if created event is still there
|
||||
final TrackedEvents trackedEvents = trackedEventsApi.getTrackedEvents(adminCtx, true);
|
||||
|
||||
boolean hasEvents = false;
|
||||
for (final TrackedEvent event : trackedEvents.getEvents()) {
|
||||
hasEvents = true;
|
||||
Assert.assertEquals("Unexpected event base url", eventBaseUrl, event.getEventBaseUrl());
|
||||
Assert.assertEquals("Unexpected event ID", eventId, event.getEventId());
|
||||
Assert.assertEquals("Unexpected leaderboard name", leaderboardName, event.getLeaderboardName());
|
||||
Assert.assertEquals("Unexpected regatta secret", regattaSecret, event.getRegattaSecret());
|
||||
|
||||
boolean hasElements = false;
|
||||
for (final TrackedElement elem : event.getTrackedElements()) {
|
||||
hasElements = true;
|
||||
Assert.assertEquals("Unexpected boat ID", boatId, elem.getBoatId());
|
||||
Assert.assertEquals("Unexpected competitor ID", competitorId, elem.getCompetitorId());
|
||||
Assert.assertEquals("Unexpected device ID", deviceId, elem.getDeviceId());
|
||||
Assert.assertEquals("Unexpected mark ID", markId, elem.getMarkId());
|
||||
}
|
||||
Assert.assertTrue("Expected at least one element", hasElements);
|
||||
}
|
||||
|
||||
Assert.assertTrue("Expected at least one event", hasEvents);
|
||||
return eventId;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeleteEvent() {
|
||||
final ApiContext adminCtx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT);
|
||||
final String leaderboardName = "TestEvent-" + UUID.randomUUID().toString();
|
||||
final String eventId = testCreateTrackedEventWithTracking(UUID.randomUUID().toString(), null, null, adminCtx,
|
||||
leaderboardName);
|
||||
|
||||
trackedEventsApi.deleteEventTrackings(adminCtx, eventId, leaderboardName);
|
||||
|
||||
final TrackedEvents trackedEvents = trackedEventsApi.getTrackedEvents(adminCtx, true);
|
||||
Assert.assertNotNull("Tracked Events element should not be null", trackedEvents);
|
||||
Assert.assertTrue("Expected empty list of tracked events", Util.isEmpty(trackedEvents.getEvents()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultipleUsers() {
|
||||
|
||||
// create test user
|
||||
final ApiContext adminSecurityCtx = createAdminApiContext(getContextRoot(), ApiContext.SECURITY_CONTEXT);
|
||||
securityApi.createUser(adminSecurityCtx, "tuser", "Test User", null, "testuser");
|
||||
final ApiContext userCtx = createApiContext(getContextRoot(), SERVER_CONTEXT, "tuser", "testuser");
|
||||
|
||||
final ApiContext adminCtx = createAdminApiContext(getContextRoot(), SERVER_CONTEXT);
|
||||
|
||||
// add an event in the user context of admin user
|
||||
testCreateTrackedEventWithTracking(null, UUID.randomUUID().toString(), null, adminCtx,
|
||||
"TestEvent-" + UUID.randomUUID().toString());
|
||||
|
||||
// check if event is invisible to the other user
|
||||
final TrackedEvents trackedEvents = trackedEventsApi.getTrackedEvents(userCtx, true);
|
||||
Assert.assertNotNull("Tracked Events element should not be null", trackedEvents);
|
||||
Assert.assertTrue("Expected empty list of tracked events", Util.isEmpty(trackedEvents.getEvents()));
|
||||
}
|
||||
|
||||
}
|
||||
+6
-2
@@ -349,13 +349,17 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
@Path("{regattaname}/competitors/{competitorid}/add")
|
||||
public Response addCompetitor(@PathParam("regattaname") String regattaName,
|
||||
@PathParam("competitorid") String competitorIdAsString) {
|
||||
@PathParam("competitorid") String competitorIdAsString,
|
||||
@QueryParam("secret") String registrationLinkSecret) {
|
||||
Response response;
|
||||
Regatta regatta = findRegattaByName(regattaName);
|
||||
if (regatta == null) {
|
||||
response = getBadRegattaErrorResponse(regattaName);
|
||||
} else {
|
||||
getSecurityService().checkCurrentUserUpdatePermission(regatta);
|
||||
boolean skipPermissionCheck = getService().skipChecksDueToCorrectSecret(regattaName, registrationLinkSecret);
|
||||
if (!skipPermissionCheck) {
|
||||
getSecurityService().checkCurrentUserUpdatePermission(regatta);
|
||||
}
|
||||
Serializable competitorId;
|
||||
try {
|
||||
competitorId = UUID.fromString(competitorIdAsString);
|
||||
|
||||
+1
@@ -35,6 +35,7 @@ public class RestApiApplication extends Application {
|
||||
classes.add(TagsResource.class);
|
||||
classes.add(UserGroupsResource.class);
|
||||
classes.add(StatusResource.class);
|
||||
classes.add(TrackedEventsResource.class);
|
||||
|
||||
// Exception Mappers
|
||||
classes.add(ShiroAuthorizationExceptionTo401ResponseMapper.class);
|
||||
|
||||
+314
@@ -0,0 +1,314 @@
|
||||
package com.sap.sailing.server.gateway.jaxrs.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import javax.ws.rs.Consumes;
|
||||
import javax.ws.rs.DELETE;
|
||||
import javax.ws.rs.FormParam;
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.POST;
|
||||
import javax.ws.rs.PUT;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
import javax.ws.rs.Produces;
|
||||
import javax.ws.rs.QueryParam;
|
||||
import javax.ws.rs.core.MediaType;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.ResponseBuilder;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
import org.json.simple.JSONArray;
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.JSONValue;
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException;
|
||||
import com.sap.sailing.server.gateway.deserialization.impl.Helpers;
|
||||
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
|
||||
import com.sap.sailing.server.impl.preferences.model.SailingPreferences;
|
||||
import com.sap.sailing.server.impl.preferences.model.TrackedElementWithDeviceId;
|
||||
import com.sap.sailing.server.impl.preferences.model.TrackedEventPreference;
|
||||
import com.sap.sailing.server.impl.preferences.model.TrackedEventPreferences;
|
||||
import com.sap.sse.security.shared.impl.User;
|
||||
|
||||
@Path("/v1/trackedevents/")
|
||||
public class TrackedEventsResource extends AbstractSailingServerResource {
|
||||
|
||||
private static final String KEY_QUERY_INCLUDE_ARCHIVED = "includeArchived";
|
||||
|
||||
private static final String KEY_LEADERBOARD_NAME = "leaderboardName";
|
||||
private static final String KEY_EVENT_ID = "eventId";
|
||||
private static final String KEY_EVENT_IS_ARCHIVED = "isArchived";
|
||||
private static final String KEY_EVENT_TRACKED_ELEMENTS = "trackedElements";
|
||||
private static final String KEY_EVENT_BASE_URL = "url";
|
||||
private static final String KEY_EVENT_REGATTA_SECRET = "regattaSecret";
|
||||
private static final String KEY_TRACKED_ELEMENT_DEVICE_ID = "deviceId";
|
||||
private static final String KEY_TRACKED_ELEMENT_COMPETITOR_ID = "competitorId";
|
||||
private static final String KEY_TRACKED_ELEMENT_BOAT_ID = "boatId";
|
||||
private static final String KEY_TRACKED_ELEMENT_MARK_ID = "markId";
|
||||
private static final String KEY_TRACKED_EVENTS = "trackedEvents";
|
||||
|
||||
@GET
|
||||
@Produces("application/json;charset=UTF-8")
|
||||
public Response getTrackedEvents(@QueryParam(KEY_QUERY_INCLUDE_ARCHIVED) String includeArchivedStr) {
|
||||
|
||||
final User currentUser = getSecurityService().getCurrentUser();
|
||||
final ResponseBuilder builder;
|
||||
|
||||
// check if user logged in
|
||||
if (currentUser != null) {
|
||||
|
||||
// load tracked events from storage
|
||||
final TrackedEventPreferences prefs = getSecurityService().getPreferenceObject(currentUser.getName(),
|
||||
SailingPreferences.TRACKED_EVENTS_PREFERENCES);
|
||||
|
||||
final JSONArray result = new JSONArray();
|
||||
final boolean includeArchived = Boolean.parseBoolean(includeArchivedStr);
|
||||
|
||||
if (prefs != null) {
|
||||
// iterate all stored tracked events
|
||||
for (final TrackedEventPreference pref : prefs.getTrackedEvents()) {
|
||||
|
||||
if (!includeArchived && pref.getIsArchived()) {
|
||||
// skip, if event is archived and should be filtered out
|
||||
continue;
|
||||
}
|
||||
|
||||
final JSONObject jsonEvent = new JSONObject();
|
||||
jsonEvent.put(KEY_EVENT_ID, pref.getEventId().toString());
|
||||
jsonEvent.put(KEY_EVENT_REGATTA_SECRET, pref.getRegattaSecret());
|
||||
jsonEvent.put(KEY_EVENT_BASE_URL, pref.getBaseUrl());
|
||||
jsonEvent.put(KEY_EVENT_IS_ARCHIVED, pref.getIsArchived());
|
||||
jsonEvent.put(KEY_LEADERBOARD_NAME, pref.getLeaderboardName());
|
||||
jsonEvent.put(KEY_EVENT_TRACKED_ELEMENTS, trackedElementsToJson(pref));
|
||||
result.add(jsonEvent);
|
||||
}
|
||||
}
|
||||
|
||||
// storage was empty -> respond with empty list
|
||||
final JSONObject resultEvents = new JSONObject();
|
||||
resultEvents.put(KEY_TRACKED_EVENTS, result);
|
||||
final String jsonString = resultEvents.toJSONString();
|
||||
builder = Response.ok(jsonString).header("Content-Type", MediaType.APPLICATION_JSON + ";charset=UTF-8");
|
||||
} else {
|
||||
builder = Response.status(Status.UNAUTHORIZED);
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
private JSONArray trackedElementsToJson(final TrackedEventPreference pref) {
|
||||
final JSONArray deviceIdsWithTrackedElementJson = new JSONArray();
|
||||
for (final TrackedElementWithDeviceId trackedElement : pref.getTrackedElements()) {
|
||||
final JSONObject trackedElementJson = new JSONObject();
|
||||
trackedElementJson.put(KEY_TRACKED_ELEMENT_DEVICE_ID, trackedElement.getDeviceId());
|
||||
if (trackedElement.getTrackedCompetitorId() != null) {
|
||||
trackedElementJson.put(KEY_TRACKED_ELEMENT_COMPETITOR_ID,
|
||||
trackedElement.getTrackedCompetitorId().toString());
|
||||
} else if (trackedElement.getTrackedBoatId() != null) {
|
||||
trackedElementJson.put(KEY_TRACKED_ELEMENT_BOAT_ID, trackedElement.getTrackedBoatId().toString());
|
||||
} else if (trackedElement.getTrackedMarkId() != null) {
|
||||
trackedElementJson.put(KEY_TRACKED_ELEMENT_MARK_ID, trackedElement.getTrackedMarkId().toString());
|
||||
}
|
||||
deviceIdsWithTrackedElementJson.add(trackedElementJson);
|
||||
}
|
||||
return deviceIdsWithTrackedElementJson;
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Path("{eventId}/{leaderboardName}")
|
||||
public Response updateOrCreateTrackedEvent(@PathParam(KEY_EVENT_ID) String eventId,
|
||||
@PathParam(KEY_LEADERBOARD_NAME) String leaderboardName, String jsonBody) {
|
||||
ResponseBuilder responseBuilder = null;
|
||||
final User currentUser = getSecurityService().getCurrentUser();
|
||||
|
||||
if (currentUser != null) {
|
||||
|
||||
try {
|
||||
final Object requestBody = JSONValue.parseWithException(jsonBody);
|
||||
final JSONObject requestObject = Helpers.toJSONObjectSafe(requestBody);
|
||||
final Boolean archived = (Boolean) requestObject.get(KEY_EVENT_IS_ARCHIVED);
|
||||
final String baseUrl = (String) requestObject.get(KEY_EVENT_BASE_URL);
|
||||
final String regattaSecret = (String) requestObject.get(KEY_EVENT_REGATTA_SECRET);
|
||||
try {
|
||||
final UUID uuidEvent = UUID.fromString(eventId);
|
||||
final boolean isArchived = archived;
|
||||
|
||||
TrackedEventPreferences prefs = getSecurityService().getPreferenceObject(currentUser.getName(),
|
||||
SailingPreferences.TRACKED_EVENTS_PREFERENCES);
|
||||
|
||||
if (prefs == null) {
|
||||
prefs = new TrackedEventPreferences();
|
||||
}
|
||||
|
||||
final JSONArray trackedElementsJson = (JSONArray) requestObject.get(KEY_EVENT_TRACKED_ELEMENTS);
|
||||
|
||||
// check that exactly one trackedElement is in JSON
|
||||
if (trackedElementsJson == null || trackedElementsJson.size() == 0) {
|
||||
// too few children
|
||||
responseBuilder = Response.status(Status.BAD_REQUEST)
|
||||
.entity("Invalid JSON body in request: Tracked element is missing.");
|
||||
} else {
|
||||
|
||||
// Copy tracked event preference objects to new list while removing the changed
|
||||
// event/leaderboard node
|
||||
final Collection<TrackedEventPreference> prefsNew = //
|
||||
StreamSupport.stream(prefs.getTrackedEvents().spliterator(), false)
|
||||
.filter(pref -> !(pref.getEventId().equals(uuidEvent)
|
||||
&& pref.getLeaderboardName().equals(leaderboardName)))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
// Create new tracked elements for this event/leaderboard from JSON body
|
||||
final Collection<TrackedElementWithDeviceId> trackedElements = new ArrayList<>();
|
||||
|
||||
for (int i = 0; i < trackedElementsJson.size(); i++) {
|
||||
final JSONObject jsonTrackedElement = (JSONObject) trackedElementsJson.get(i);
|
||||
|
||||
// parse IDs
|
||||
final String deviceId = (String) jsonTrackedElement.get(KEY_TRACKED_ELEMENT_DEVICE_ID);
|
||||
final String competitorIdStr = (String) jsonTrackedElement
|
||||
.get(KEY_TRACKED_ELEMENT_COMPETITOR_ID);
|
||||
final String boatIdStr = (String) jsonTrackedElement.get(KEY_TRACKED_ELEMENT_BOAT_ID);
|
||||
final String markIdStr = (String) jsonTrackedElement.get(KEY_TRACKED_ELEMENT_MARK_ID);
|
||||
|
||||
// parse UUIDs of tracked element
|
||||
final UUID competitorId = parseUUID(competitorIdStr);
|
||||
final UUID boatId = parseUUID(boatIdStr);
|
||||
final UUID markId = parseUUID(markIdStr);
|
||||
|
||||
if (boatId != null ^ markId != null ^ competitorId != null) {
|
||||
|
||||
// create TrackedElementWithID holder
|
||||
final TrackedElementWithDeviceId newPrefElem = new TrackedElementWithDeviceId(deviceId,
|
||||
boatId, competitorId, markId);
|
||||
trackedElements.add(newPrefElem);
|
||||
|
||||
} else {
|
||||
// no boatId, competitorId or markId were specified
|
||||
responseBuilder = Response.status(Status.BAD_REQUEST)
|
||||
.entity("Invalid JSON body in request.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final TrackedEventPreference newPreference = new TrackedEventPreference(uuidEvent,
|
||||
leaderboardName, trackedElements, baseUrl, isArchived, regattaSecret);
|
||||
prefsNew.add(newPreference);
|
||||
|
||||
prefs.setTrackedEvents(prefsNew);
|
||||
|
||||
getSecurityService().setPreferenceObject(currentUser.getName(),
|
||||
SailingPreferences.TRACKED_EVENTS_PREFERENCES, prefs);
|
||||
responseBuilder = Response.status(Status.ACCEPTED);
|
||||
}
|
||||
|
||||
} catch (IllegalArgumentException | ClassCastException e) {
|
||||
responseBuilder = Response.status(Status.BAD_REQUEST)
|
||||
.entity("Invalid or missing attributes in JSON body.");
|
||||
}
|
||||
} catch (ParseException | JsonDeserializationException e) {
|
||||
responseBuilder = Response.status(Status.BAD_REQUEST).entity("Invalid JSON body in request.");
|
||||
}
|
||||
} else {
|
||||
responseBuilder = Response.status(Status.UNAUTHORIZED);
|
||||
}
|
||||
return responseBuilder.build();
|
||||
}
|
||||
|
||||
private UUID parseUUID(String potentialUUID) {
|
||||
UUID uuid;
|
||||
if (potentialUUID != null && !potentialUUID.isEmpty()) {
|
||||
|
||||
try {
|
||||
uuid = UUID.fromString(potentialUUID);
|
||||
} catch (IllegalArgumentException e) {
|
||||
uuid = null;
|
||||
}
|
||||
} else {
|
||||
uuid = null;
|
||||
}
|
||||
return uuid;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("{eventId}/{leaderboardName}")
|
||||
public Response updateTrackedEventsArchivedStatus(@PathParam(KEY_EVENT_ID) String eventId,
|
||||
@PathParam(KEY_LEADERBOARD_NAME) String leaderboardName,
|
||||
@FormParam(KEY_QUERY_INCLUDE_ARCHIVED) String archived) {
|
||||
final boolean isArchived = Boolean.parseBoolean(archived);
|
||||
return applyOnEventByIdAndLeadboardName(eventId, leaderboardName,
|
||||
pref -> new TrackedEventPreference(pref, isArchived));
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("{eventId}/{leaderboardName}")
|
||||
public Response deleteTrackedEvents(@PathParam(KEY_EVENT_ID) String eventId,
|
||||
@PathParam(KEY_LEADERBOARD_NAME) String leaderboardName) {
|
||||
return applyOnEventByIdAndLeadboardName(eventId, leaderboardName, pref -> null);
|
||||
}
|
||||
|
||||
/** Finds the tracked event with the corresponding eventId and applies the function to it. */
|
||||
private Response applyOnEventByIdAndLeadboardName(String eventId, String leaderboardName,
|
||||
Function<TrackedEventPreference, TrackedEventPreference> function) {
|
||||
ResponseBuilder responseBuilder;
|
||||
final User currentUser = getSecurityService().getCurrentUser();
|
||||
|
||||
if (currentUser != null) {
|
||||
|
||||
final TrackedEventPreferences prefs = getSecurityService().getPreferenceObject(currentUser.getName(),
|
||||
SailingPreferences.TRACKED_EVENTS_PREFERENCES);
|
||||
try {
|
||||
// parse tracked event UUID
|
||||
final UUID eventUuid = UUID.fromString(eventId);
|
||||
|
||||
if (prefs == null) {
|
||||
// storage is empty
|
||||
responseBuilder = Response.status(Status.NOT_FOUND)
|
||||
.entity("No tracked events with this eventId found.");
|
||||
} else {
|
||||
|
||||
// iterate stored preferences until tracked event with corresponding eventId is found
|
||||
boolean found = false;
|
||||
final Collection<TrackedEventPreference> newPrefs = new HashSet<>();
|
||||
for (final TrackedEventPreference pref : prefs.getTrackedEvents()) {
|
||||
if (pref.getEventId().equals(eventUuid) && pref.getLeaderboardName().equals(leaderboardName)) {
|
||||
// tracked event found, apply function on it and remove it if result of function execution
|
||||
// is null
|
||||
found = true;
|
||||
final TrackedEventPreference updatedPreference = function.apply(pref);
|
||||
if (updatedPreference != null) {
|
||||
newPrefs.add(updatedPreference);
|
||||
}
|
||||
} else {
|
||||
newPrefs.add(pref);
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
// tracked event was not found -> respond with error
|
||||
responseBuilder = Response.status(Status.NOT_FOUND)
|
||||
.entity("No tracked events with this eventId found.");
|
||||
} else {
|
||||
// update and store preferences
|
||||
prefs.setTrackedEvents(newPrefs);
|
||||
getSecurityService().setPreferenceObject(currentUser.getName(),
|
||||
SailingPreferences.TRACKED_EVENTS_PREFERENCES, prefs);
|
||||
responseBuilder = Response.status(Status.ACCEPTED);
|
||||
}
|
||||
|
||||
}
|
||||
} catch (IllegalArgumentException e) {
|
||||
// eventId parameter could not be parsed
|
||||
responseBuilder = Response.status(Status.BAD_REQUEST).entity("Invalid eventId.");
|
||||
}
|
||||
} else {
|
||||
responseBuilder = Response.status(Status.UNAUTHORIZED);
|
||||
}
|
||||
return responseBuilder.build();
|
||||
}
|
||||
}
|
||||
@@ -154,7 +154,7 @@ name to make sure you get to the event you're interested in.
|
||||
<td>Creates a competitor and adds it to the regatta</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>/api/v1/regattas/{regattaname}/competitors/{competitorId}/add</td>
|
||||
<td><a href="regattasCompetitorAdd.html">/api/v1/regattas/{regattaname}/competitors/{competitorId}/add</a></td>
|
||||
<td>Registers a competitor with a regatta (POST)</td>
|
||||
</tr>
|
||||
<tr>
|
||||
@@ -340,6 +340,22 @@ name to make sure you get to the event you're interested in.
|
||||
<td><a href="status.html">/api/v1/status</a></td>
|
||||
<td>Returns a status description for the "RacingEventService" core service</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="trackedeventsGetDoc.html">/api/v1/trackedevents</a></td>
|
||||
<td>Gets all tracked events with the associated device-to-competitor/boat/mark elements for the current user.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="trackedeventsPutDoc.html">/api/v1/trackedevents</a></td>
|
||||
<td>Create or update a tracked event for the current user.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="trackedeventsPostDoc.html">/api/v1/trackedevents</a></td>
|
||||
<td>Update the archived status of a tracked event for the current user.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="trackedeventsDeleteDoc.html">/api/v1/trackedevents</a></td>
|
||||
<td>Delete a tracked event for the current user.</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
<!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/regattas/{regattaname}/competitors/{competitorId}/add</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Registers an existing competitor with the regatta.<br/>
|
||||
This provides functionality <li> for the regatta owner (or any user with update permission on the regatta) to add existing
|
||||
competitors to the regatta <li> and for any user to register himself to an regatta that is explicitly open for self-registrations.<br/>
|
||||
All slashes contained within the {regattaname} parameter must be replaced by "__" (double underscore).</p>
|
||||
<br/>
|
||||
<table>
|
||||
<tr>
|
||||
<td>Webservice Type:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Output format:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Mandatory parameters:</td>
|
||||
<td>
|
||||
<div>regattaname the name of the regatta to register a competitor for</div>
|
||||
<div>competitorid the UUID of the competitor to register</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Additional mandatory parameters for user self-registrations on open regattas:</td>
|
||||
<td>
|
||||
<div>secret secret string required for self registration validation purpose</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,52 @@
|
||||
<!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/trackedevents/{eventId}/{leaderboardName}</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Delete a tracked event for the current user.</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>Path parameter eventId: Identifier of the associated event<br />
|
||||
Path parameter leaderboardName: Name of the associated leaderboard
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>DELETE</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td>Request:
|
||||
http://www.sapsailing.com/sailingserver/api/v1/trackedevents?eventId=71fe232b-19b8-455f-ac91-23a53a5c557a&leaderbaordName=TestRegattaLeaderboard<br />
|
||||
Status 202 <br />
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,53 @@
|
||||
<!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/trackedevents</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Gets all tracked events with the associated
|
||||
device-to-competitor/boat/mark elements for the current user.</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>includeArchived: Should be set to true, if the archived
|
||||
elements should be returned as well. Default value false returns
|
||||
only non-archived elements.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>GET</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td><a
|
||||
href="http://www.sapsailing.com/sailingserver/api/v1/trackedevents?includeArchived=false">http://www.sapsailing.com/sailingserver/api/v1/trackedevents?includeArchived=false</a><br />
|
||||
Status 200</td>
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,55 @@
|
||||
<!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/trackedevents</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Update the archived status of a tracked event for the current
|
||||
user.</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>eventId (Path parameter): Identifier of the associated event<br />
|
||||
leaderboardName (Path parameter): Name of the associated leaderboard<br />
|
||||
isArchived (Form parameter): True or False depending on whether the
|
||||
event should be set to archived or non-archived.
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>POST</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td>Request:
|
||||
http://www.sapsailing.com/sailingserver/api/v1/trackedevents/71fe232b-19b8-455f-ac91-23a53a5c557a/TestRegattaLeaderboard<br />
|
||||
form: isArchived=true<br /> Status 202 <br />
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,58 @@
|
||||
<!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/trackedevents/{eventId}/{leaderboardName}</h2>
|
||||
|
||||
<b>Description:</b>
|
||||
<p>Create or update a tracked event for the current user and set (overwrite) all tracked elements.</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>Path Parameter eventId: Identifier of the associated event<br />
|
||||
Path Parameter leaderboardName: Name of the associated leaderboard
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Optional parameters:</td>
|
||||
<td>None</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request method:</td>
|
||||
<td>PUT</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Example:</td>
|
||||
<td>Request:
|
||||
http://www.sapsailing.com/sailingserver/api/v1/trackedevents/61fe232b-19b8-455f-ac91-23a53a5c557a/TestRegattaLeaderboard<br />
|
||||
<br /> Body (device-to-competitor):<br />
|
||||
{"url":"http://www.sapsailing.com","regattaSecret":"71fe232b-19b8-455f-ac91-23a53a5c557a","trackedElements":[{"deviceId":"301eda00-68ad-402d-9734-4a7d5b8df406","competitorId":"6204e6d2-2d6c-4891-b077-d83117c69513"}],"isArchived":false}<br />
|
||||
<br /> Body(device-to-boat):<br />
|
||||
{"url":"http://www.sapsailing.com","regattaSecret":"71fe232b-19b8-455f-ac91-23a53a5c557a","trackedElements":[{"deviceId":"301eda00-68ad-402d-9734-4a7d5b8df406","boatId":"6204e6d2-2d6c-4891-b077-d83117c69513"}],"isArchived":false}<br />
|
||||
<br /> Body (device-to-mark):<br />
|
||||
{"url":"http://www.sapsailing.com","regattaSecret":"71fe232b-19b8-455f-ac91-23a53a5c557a","trackedElements":[{"deviceId":"301eda00-68ad-402d-9734-4a7d5b8df406","markId":"6204e6d2-2d6c-4891-b077-d83117c69513"}],"isArchived":false}<br />
|
||||
Status 202 <br />
|
||||
</tr>
|
||||
</table>
|
||||
<div style="height: 1em;"></div>
|
||||
<a href="index.html">Back to Web Service Overview</a>
|
||||
</body>
|
||||
</html>
|
||||
@@ -46,6 +46,7 @@ import com.sap.sailing.server.impl.preferences.model.BoatClassNotificationPrefer
|
||||
import com.sap.sailing.server.impl.preferences.model.CompetitorNotificationPreferences;
|
||||
import com.sap.sailing.server.impl.preferences.model.SailorProfilePreferences;
|
||||
import com.sap.sailing.server.impl.preferences.model.StoredDataMiningQueryPreferences;
|
||||
import com.sap.sailing.server.impl.preferences.model.TrackedEventPreferences;
|
||||
import com.sap.sailing.server.interfaces.RacingEventService;
|
||||
import com.sap.sailing.server.notification.impl.SailingNotificationServiceImpl;
|
||||
import com.sap.sailing.server.security.SailingViewerRole;
|
||||
@@ -153,12 +154,15 @@ public class Activator implements BundleActivator {
|
||||
properties));
|
||||
properties.put(PreferenceConverter.KEY_PARAMETER_NAME, StoredDataMiningQueryPreferences.PREF_NAME);
|
||||
registrations.add(context.registerService(PreferenceConverter.class,
|
||||
new GenericJSONPreferenceConverter<>(() -> new StoredDataMiningQueryPreferences()), properties));
|
||||
new GenericJSONPreferenceConverter<>(StoredDataMiningQueryPreferences::new), properties));
|
||||
properties.put(PreferenceConverter.KEY_PARAMETER_NAME, SailorProfilePreferences.PREF_NAME);
|
||||
registrations.add(context.registerService(PreferenceConverter.class,
|
||||
new GenericJSONPreferenceConverter<>(
|
||||
() -> new SailorProfilePreferences(racingEventService.getCompetitorAndBoatStore())),
|
||||
properties));
|
||||
properties.put(PreferenceConverter.KEY_PARAMETER_NAME, TrackedEventPreferences.PREF_NAME);
|
||||
registrations.add(context.registerService(PreferenceConverter.class,
|
||||
new GenericJSONPreferenceConverter<>(TrackedEventPreferences::new), properties));
|
||||
}
|
||||
|
||||
public static BundleContext getContext() {
|
||||
|
||||
+1
@@ -7,4 +7,5 @@ package com.sap.sailing.server.impl.preferences.model;
|
||||
public interface SailingPreferences {
|
||||
public static final String NOTIFICATION_PREFERENCES_PREFIX = "sailing.notifications.";
|
||||
public static final String STORED_DATAMINING_QUERY_PREFERENCES = "sailing.datamining.storedqueries";
|
||||
public static final String TRACKED_EVENTS_PREFERENCES = "sailing.profile.trackedevents";
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
package com.sap.sailing.server.impl.preferences.model;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sse.common.settings.generic.AbstractGenericSerializableSettings;
|
||||
import com.sap.sse.common.settings.generic.StringSetting;
|
||||
import com.sap.sse.common.settings.generic.UUIDSetting;
|
||||
|
||||
public class TrackedElementWithDeviceId extends AbstractGenericSerializableSettings {
|
||||
|
||||
private static final long serialVersionUID = 234711768869820003L;
|
||||
|
||||
private transient StringSetting deviceId;
|
||||
private transient UUIDSetting trackedCompetitorId;
|
||||
private transient UUIDSetting trackedBoatId;
|
||||
private transient UUIDSetting trackedMarkId;
|
||||
|
||||
public TrackedElementWithDeviceId() {
|
||||
}
|
||||
|
||||
/** copy constructor */
|
||||
public TrackedElementWithDeviceId(TrackedElementWithDeviceId other) {
|
||||
this();
|
||||
this.deviceId.setValue(other.getDeviceId());
|
||||
this.trackedBoatId.setValue(other.getTrackedBoatId());
|
||||
this.trackedCompetitorId.setValue(other.getTrackedCompetitorId());
|
||||
this.trackedMarkId.setValue(other.getTrackedMarkId());
|
||||
}
|
||||
|
||||
public TrackedElementWithDeviceId(String deviceId, UUID trackedBoatId, UUID trackedCompetitorId,
|
||||
UUID trackedMarkId) {
|
||||
this();
|
||||
this.deviceId.setValue(deviceId);
|
||||
this.trackedBoatId.setValue(trackedBoatId);
|
||||
this.trackedCompetitorId.setValue(trackedCompetitorId);
|
||||
this.trackedMarkId.setValue(trackedMarkId);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addChildSettings() {
|
||||
deviceId = new StringSetting("deviceId", this);
|
||||
trackedCompetitorId = new UUIDSetting("trackedCompetitorId", this);
|
||||
trackedBoatId = new UUIDSetting("trackedBoatId", this);
|
||||
trackedMarkId = new UUIDSetting("trackedMarkId", this);
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId.getValue();
|
||||
}
|
||||
|
||||
public UUID getTrackedBoatId() {
|
||||
return trackedBoatId.getValue();
|
||||
}
|
||||
|
||||
public UUID getTrackedCompetitorId() {
|
||||
return trackedCompetitorId.getValue();
|
||||
}
|
||||
|
||||
public UUID getTrackedMarkId() {
|
||||
return trackedMarkId.getValue();
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package com.sap.sailing.server.impl.preferences.model;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.settings.generic.AbstractGenericSerializableSettings;
|
||||
import com.sap.sse.common.settings.generic.BooleanSetting;
|
||||
import com.sap.sse.common.settings.generic.SettingsList;
|
||||
import com.sap.sse.common.settings.generic.StringSetting;
|
||||
import com.sap.sse.common.settings.generic.UUIDSetting;
|
||||
|
||||
public class TrackedEventPreference extends AbstractGenericSerializableSettings {
|
||||
private static final long serialVersionUID = 234711768869820003L;
|
||||
|
||||
private transient UUIDSetting eventId;
|
||||
private transient StringSetting leaderboardName;
|
||||
private transient StringSetting baseUrl;
|
||||
private transient BooleanSetting isArchived;
|
||||
private transient StringSetting regattaSecret;
|
||||
|
||||
private transient SettingsList<TrackedElementWithDeviceId> trackedElements;
|
||||
|
||||
public TrackedEventPreference() {
|
||||
}
|
||||
|
||||
/** copy constructor */
|
||||
public TrackedEventPreference(TrackedEventPreference other) {
|
||||
this();
|
||||
eventId.setValue(other.getEventId());
|
||||
leaderboardName.setValue(other.getLeaderboardName());
|
||||
trackedElements.setValues(other.getTrackedElements());
|
||||
baseUrl.setValue(other.getBaseUrl());
|
||||
isArchived.setValue(other.getIsArchived());
|
||||
this.regattaSecret.setValue(other.getRegattaSecret());
|
||||
}
|
||||
|
||||
/** copy constructor with new archived state */
|
||||
public TrackedEventPreference(TrackedEventPreference other, boolean isArchived) {
|
||||
this();
|
||||
eventId.setValue(other.getEventId());
|
||||
leaderboardName.setValue(other.getLeaderboardName());
|
||||
trackedElements.setValues(other.getTrackedElements());
|
||||
baseUrl.setValue(other.getBaseUrl());
|
||||
this.isArchived.setValue(isArchived);
|
||||
this.regattaSecret.setValue(other.getRegattaSecret());
|
||||
}
|
||||
|
||||
/** copy constructor with additional tracked event */
|
||||
public TrackedEventPreference(TrackedEventPreference other, TrackedElementWithDeviceId trackedElement) {
|
||||
this();
|
||||
eventId.setValue(other.getEventId());
|
||||
leaderboardName.setValue(other.getLeaderboardName());
|
||||
final Collection<TrackedElementWithDeviceId> trackedElements = new HashSet<>();
|
||||
Util.addAll(other.getTrackedElements(), trackedElements);
|
||||
trackedElements.add(trackedElement);
|
||||
this.trackedElements.setValues(trackedElements);
|
||||
baseUrl.setValue(other.getBaseUrl());
|
||||
this.isArchived.setValue(other.getIsArchived());
|
||||
this.regattaSecret.setValue(other.getRegattaSecret());
|
||||
}
|
||||
|
||||
public TrackedEventPreference(UUID eventId, String leaderboardName,
|
||||
Iterable<TrackedElementWithDeviceId> trackedElements, String baseUrl, boolean isArchived,
|
||||
String regattaSecret) {
|
||||
this();
|
||||
this.eventId.setValue(eventId);
|
||||
this.leaderboardName.setValue(leaderboardName);
|
||||
this.trackedElements.setValues(trackedElements);
|
||||
this.baseUrl.setValue(baseUrl);
|
||||
this.isArchived.setValue(isArchived);
|
||||
this.regattaSecret.setValue(regattaSecret);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addChildSettings() {
|
||||
eventId = new UUIDSetting("eventId", this);
|
||||
leaderboardName = new StringSetting("leaderboardName", this);
|
||||
trackedElements = new SettingsList<TrackedElementWithDeviceId>("trackedElements", this,
|
||||
() -> new TrackedElementWithDeviceId());
|
||||
baseUrl = new StringSetting("baseUrl", this);
|
||||
isArchived = new BooleanSetting("isArchived", this);
|
||||
regattaSecret = new StringSetting("regattaSecret", this);
|
||||
}
|
||||
|
||||
public UUID getEventId() {
|
||||
return eventId.getValue();
|
||||
}
|
||||
|
||||
public String getLeaderboardName() {
|
||||
return leaderboardName.getValue();
|
||||
}
|
||||
|
||||
public Iterable<TrackedElementWithDeviceId> getTrackedElements() {
|
||||
return trackedElements.getValues();
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl.getValue();
|
||||
}
|
||||
|
||||
public boolean getIsArchived() {
|
||||
return isArchived.getValue();
|
||||
}
|
||||
|
||||
public String getRegattaSecret() {
|
||||
return regattaSecret.getValue();
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.sap.sailing.server.impl.preferences.model;
|
||||
|
||||
import com.sap.sse.common.settings.generic.AbstractGenericSerializableSettings;
|
||||
import com.sap.sse.common.settings.generic.SettingsList;
|
||||
|
||||
public class TrackedEventPreferences extends AbstractGenericSerializableSettings {
|
||||
|
||||
private static final long serialVersionUID = -290209497308646446L;
|
||||
|
||||
// TODO: setting preference title
|
||||
public static final String PREF_NAME = SailingPreferences.TRACKED_EVENTS_PREFERENCES;
|
||||
|
||||
private transient SettingsList<TrackedEventPreference> trackedEvents;
|
||||
|
||||
public TrackedEventPreferences() {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addChildSettings() {
|
||||
trackedEvents = new SettingsList<>("trackedEvents", this, TrackedEventPreference::new);
|
||||
}
|
||||
|
||||
public Iterable<TrackedEventPreference> getTrackedEvents() {
|
||||
return trackedEvents.getValues();
|
||||
}
|
||||
|
||||
public void setTrackedEvents(Iterable<TrackedEventPreference> trackedEvents) {
|
||||
this.trackedEvents.setValues(trackedEvents);
|
||||
}
|
||||
}
|
||||
@@ -21,6 +21,12 @@
|
||||
<div class="mainContent">
|
||||
<h2 class="releaseHeadline">Release Notes - Administration Console</h2>
|
||||
<div class="innerContent">
|
||||
<h2 class="articleSubheadline">July 2019</h2>
|
||||
<ul class="bulletList">
|
||||
<li>Result and competitor import URLs are now only considered when the requesting user
|
||||
has the permission to read that URL.</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="articleSubheadline">June 2019</h2>
|
||||
<ul class="bulletList">
|
||||
<li>Added new API endpoints for creating, reading, updating and deleting roles under /restsecurity/role.
|
||||
@@ -35,7 +41,12 @@
|
||||
<li>Blue flag up/down times can be cleared in the AdminConsole now</li>
|
||||
<li>Users can now create media tracks if they have the permissions based on the ownership that
|
||||
will result from the creation process.</li>
|
||||
<li>A couple of new API end points for managing tracked events were added under /api/v1/trackedevents/.
|
||||
The new end points support creation, reading, updating and deleting tracked events and their
|
||||
associated device-competitor/boat/mark elements for the current user.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="articleSubheadline">May 2019</h2>
|
||||
<ul class="bulletList">
|
||||
<li>Ranks (finishing orders) entered by the Race Manager app user are no longer
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.sap.sse.common.impl;
|
||||
|
||||
import com.sap.sse.common.Duration;
|
||||
|
||||
/**
|
||||
* Uses double for internal representation. Therefore this implementation achieves a higher accuracy but uses more
|
||||
* memory in comparison to {@link MillisecondsDurationImpl}.
|
||||
*
|
||||
* @author Daniel Lisunkin (i505543)
|
||||
*
|
||||
*/
|
||||
public class SecondsDurationImpl implements Duration {
|
||||
private static final long serialVersionUID = 7575912908654324216L;
|
||||
private final double seconds;
|
||||
|
||||
public SecondsDurationImpl(double seconds) {
|
||||
super();
|
||||
this.seconds = seconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(Duration o) {
|
||||
double diff = seconds - o.asSeconds();
|
||||
return diff > 0. ? 1 : diff < 0. ? -1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public long asMillis() {
|
||||
return Math.round(seconds / Duration.ONE_MILLISECOND.asSeconds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public double asSeconds() {
|
||||
return seconds;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double asMinutes() {
|
||||
return seconds / Duration.ONE_MINUTE.asSeconds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double asHours() {
|
||||
return seconds / Duration.ONE_HOUR.asSeconds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double asDays() {
|
||||
return seconds / Duration.ONE_DAY.asSeconds();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration abs() {
|
||||
return seconds >= 0 ? this : new SecondsDurationImpl(-seconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration divide(long divisor) {
|
||||
return new SecondsDurationImpl(seconds / divisor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration divide(double divisor) {
|
||||
return new SecondsDurationImpl(seconds / divisor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public double divide(Duration duration) {
|
||||
return (seconds / duration.asSeconds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration times(long factor) {
|
||||
return new SecondsDurationImpl(seconds * factor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration times(double factor) {
|
||||
return new SecondsDurationImpl(seconds * factor);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration minus(Duration duration) {
|
||||
return new SecondsDurationImpl(seconds - duration.asSeconds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration minus(long milliseconds) {
|
||||
return new SecondsDurationImpl(seconds - (double) milliseconds / 1000.);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration plus(long milliseconds) {
|
||||
return new SecondsDurationImpl(seconds + milliseconds / 1000.);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration plus(Duration duration) {
|
||||
return new SecondsDurationImpl(seconds + duration.asSeconds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration mod(Duration d) {
|
||||
return new SecondsDurationImpl(seconds % d.asSeconds());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
StringBuffer result = new StringBuffer();
|
||||
result.append(asSeconds()).append("s");
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
}
|
||||
+3
-2
@@ -4,6 +4,8 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
/**
|
||||
* A group of users; equality and hash code are based solely on the {@link #getId() ID}.
|
||||
*/
|
||||
@@ -45,7 +47,6 @@ public abstract class AbstractUserGroupImpl<U extends UserReference, RD extends
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "AbstractUserGroupImpl [users=" + users + ", getName()=" + getName() + ", getId()=" + getId()
|
||||
+ ", getRoleDefinitionMap()=" + getRoleDefinitionMap() + "]";
|
||||
return "User group "+getName()+", "+Util.size(getUsers())+" users";
|
||||
}
|
||||
}
|
||||
@@ -116,9 +116,6 @@
|
||||
<bool name="preference_allow_edit_race_factor_default">true</bool>
|
||||
<string name="preference_protest_time_duration_key">protestTimeDuration</string>
|
||||
<integer name="preference_protest_time_duration_default">90</integer>
|
||||
<string name="preference_protest_time_duration_custom_key">protestTimeDurationCustom</string>
|
||||
<string name="preference_protest_time_duration_custom_editable_key">protestTimeDurationCustomEditable</string>
|
||||
<bool name="preference_protest_time_duration_custom_editable_default">true</bool>
|
||||
|
||||
<string name="preference_allow_demo_key" translatable="false">allow_demo</string>
|
||||
<bool name="preference_allow_demo_default">false</bool>
|
||||
|
||||
-21
@@ -523,33 +523,12 @@ public class AppPreferences {
|
||||
context.getResources().getInteger(R.integer.preference_protest_time_duration_default));
|
||||
}
|
||||
|
||||
public int getProtestTimeDurationInMinutesCustom() {
|
||||
return helper.getInt(context.getString(R.string.preference_protest_time_duration_custom_key), -1);
|
||||
}
|
||||
|
||||
public boolean isDefaultProtestTimeCustomEditable() {
|
||||
return helper.getBoolean(context.getString(R.string.preference_protest_time_duration_custom_editable_key),
|
||||
context.getResources().getBoolean(R.bool.preference_protest_time_duration_custom_editable_default));
|
||||
}
|
||||
|
||||
public void setDefaultProtestTimeDurationInMinutes(int protestTimeInMinutes) {
|
||||
helper.getEditor()
|
||||
.putInt(context.getString(R.string.preference_protest_time_duration_key), protestTimeInMinutes)
|
||||
.commit();
|
||||
}
|
||||
|
||||
public void setDefaultProtestTimeDurationInMinutesCustom(int protestTimeInMinutest) {
|
||||
helper.getEditor()
|
||||
.putInt(context.getString(R.string.preference_protest_time_duration_custom_key), protestTimeInMinutest)
|
||||
.commit();
|
||||
}
|
||||
|
||||
public void setDefaultProtestTimeDurationInMinutesCustomEditable(boolean editable) {
|
||||
helper.getEditor()
|
||||
.putBoolean(context.getString(R.string.preference_protest_time_duration_custom_editable_key), editable)
|
||||
.commit();
|
||||
}
|
||||
|
||||
public void setAccessToken(String accessToken) {
|
||||
helper.getEditor().putString(context.getString(R.string.preference_access_token_key), accessToken).commit();
|
||||
}
|
||||
|
||||
+3
-6
@@ -1,8 +1,5 @@
|
||||
package com.sap.sailing.racecommittee.app.domain.configuration.impl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
import com.sap.sailing.android.shared.logging.ExLog;
|
||||
import com.sap.sailing.domain.base.configuration.ConfigurationLoader;
|
||||
import com.sap.sailing.domain.base.configuration.RacingProcedureConfiguration;
|
||||
@@ -23,6 +20,9 @@ import com.sap.sailing.domain.common.racelog.RacingProcedureType;
|
||||
import com.sap.sailing.racecommittee.app.AppPreferences;
|
||||
import com.sap.sse.common.Duration;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
|
||||
public class PreferencesRegattaConfigurationLoader implements ConfigurationLoader<RegattaConfiguration> {
|
||||
|
||||
private static final String TAG = PreferencesRegattaConfigurationLoader.class.getSimpleName();
|
||||
@@ -113,9 +113,6 @@ public class PreferencesRegattaConfigurationLoader implements ConfigurationLoade
|
||||
if (configuration.getDefaultProtestTimeDuration() != null) {
|
||||
preferences.setDefaultProtestTimeDurationInMinutes(
|
||||
(int) configuration.getDefaultProtestTimeDuration().asMinutes());
|
||||
preferences.setDefaultProtestTimeDurationInMinutesCustomEditable(false);
|
||||
} else {
|
||||
preferences.setDefaultProtestTimeDurationInMinutesCustomEditable(true);
|
||||
}
|
||||
|
||||
if (configuration.getRRS26Configuration() != null) {
|
||||
|
||||
-1
@@ -401,7 +401,6 @@ public class LoginActivity extends BaseActivity implements EventSelectedListener
|
||||
public void onLoadFailed(Exception reason) {
|
||||
dismissProgressSpinner();
|
||||
|
||||
preferences.setDefaultProtestTimeDurationInMinutesCustomEditable(true);
|
||||
if (reason instanceof FileNotFoundException) {
|
||||
Toast.makeText(getApplicationContext(), getString(R.string.loading_configuration_not_found),
|
||||
Toast.LENGTH_LONG).show();
|
||||
|
||||
+4
-11
@@ -319,8 +319,7 @@ public class ProtestTimeDialogFragment extends AttachedDialogFragment implements
|
||||
TimePoint startTime = TimeUtils.getTime(mTimePicker);
|
||||
TimePoint now = MillisecondsTimePoint.now();
|
||||
for (ManagedRace race : selectedRaces) {
|
||||
Duration duration = Duration.ONE_MINUTE
|
||||
.times(AppPreferences.on(getActivity()).getProtestTimeDurationInMinutes());
|
||||
Duration duration = Duration.ONE_MINUTE.times(mDuration);
|
||||
TimeRange protestTime = new TimeRangeImpl(startTime, startTime.plus(duration));
|
||||
race.getState().setProtestTime(now, protestTime);
|
||||
}
|
||||
@@ -404,12 +403,6 @@ public class ProtestTimeDialogFragment extends AttachedDialogFragment implements
|
||||
private void updateProtestRange() {
|
||||
if (mDuration == null) {
|
||||
mDuration = mPreferences.getProtestTimeDurationInMinutes();
|
||||
if (mPreferences.isDefaultProtestTimeCustomEditable()) {
|
||||
int custom = mPreferences.getProtestTimeDurationInMinutesCustom();
|
||||
if (custom >= 0) {
|
||||
mDuration = custom;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (mProtestDuration != null) {
|
||||
mProtestDuration.setText(getString(R.string.protest_duration, mDuration));
|
||||
@@ -468,9 +461,9 @@ public class ProtestTimeDialogFragment extends AttachedDialogFragment implements
|
||||
builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
mDuration = Integer.parseInt(duration.getText().toString());
|
||||
if (mPreferences.isDefaultProtestTimeCustomEditable()) {
|
||||
mPreferences.setDefaultProtestTimeDurationInMinutesCustom(mDuration);
|
||||
try {
|
||||
mDuration = Integer.parseInt(duration.getText().toString());
|
||||
} catch (NumberFormatException ignored) {
|
||||
}
|
||||
updateProtestRange();
|
||||
}
|
||||
|
||||
+5
@@ -33,6 +33,7 @@ import com.sap.sailing.racecommittee.app.utils.autoupdate.AutoUpdater;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
public class GeneralPreferenceFragment extends BasePreferenceFragment {
|
||||
@@ -200,6 +201,7 @@ public class GeneralPreferenceFragment extends BasePreferenceFragment {
|
||||
//Update all the other preferences, e.g. course areas, mail recipient
|
||||
final SharedPreferences.OnSharedPreferenceChangeListener listener =
|
||||
new SharedPreferences.OnSharedPreferenceChangeListener() {
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
|
||||
final Preference preference = findPreference(key);
|
||||
@@ -211,6 +213,9 @@ public class GeneralPreferenceFragment extends BasePreferenceFragment {
|
||||
if (listener != null) {
|
||||
listener.onPreferenceChange(preference, newValue);
|
||||
}
|
||||
if (preference instanceof EditSetPreference) {
|
||||
preference.persistStringSet((Set<String>) newValue);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
+5
-1
@@ -25,6 +25,10 @@ public class IntegerEditTextPreference extends EditTextPreference {
|
||||
|
||||
@Override
|
||||
protected boolean persistString(String value) {
|
||||
return persistInt(Integer.valueOf(value));
|
||||
try {
|
||||
return persistInt(Integer.valueOf(value));
|
||||
} catch (NumberFormatException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user