@ -1,14 +1,15 @@
@@ -1,14 +1,15 @@
/* © SRSoftware 2025 */
package de.srsoftware.umbrella.stock ;
import static de.srsoftware.tools.Optionals.nullIfEmpty ;
import static de.srsoftware.umbrella.core.ConnectionProvider.connect ;
import static de.srsoftware.umbrella.core.Constants.ID ;
import static de.srsoftware.umbrella.core.Constants.NAME ;
import static de.srsoftware.umbrella.core.Constants.* ;
import static de.srsoftware.umbrella.core.ModuleRegistry.companyService ;
import static de.srsoftware.umbrella.core.ModuleRegistry.userService ;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.missingFieldException ;
import static de.srsoftware.umbrella.core.exceptions.UmbrellaException.* ;
import static de.srsoftware.umbrella.stock.Constants.* ;
import static java.lang.System.Logger.Level.WARNING ;
import static java.text.MessageFormat.format ;
import static java.util.Comparator.comparing ;
import com.sun.net.httpserver.HttpExchange ;
@ -19,18 +20,17 @@ import de.srsoftware.umbrella.core.BaseHandler;
@@ -19,18 +20,17 @@ import de.srsoftware.umbrella.core.BaseHandler;
import de.srsoftware.umbrella.core.ModuleRegistry ;
import de.srsoftware.umbrella.core.api.StockService ;
import de.srsoftware.umbrella.core.exceptions.UmbrellaException ;
import de.srsoftware.umbrella.core.model.Item ;
import de.srsoftware.umbrella.core.model.Location ;
import de.srsoftware.umbrella.core.model.Token ;
import de.srsoftware.umbrella.core.model.UmbrellaUser ;
import de.srsoftware.umbrella.core.model.* ;
import org.json.JSONObject ;
import java.io.IOException ;
import java.util.* ;
public class StockApi extends BaseHandler implements StockService {
public class StockModule extends BaseHandler implements StockService {
private final StockDb stockDb ;
public StockApi ( Configuration config ) throws UmbrellaException {
public StockModule ( Configuration config ) throws UmbrellaException {
super ( ) ;
var dbFile = config . get ( CONFIG_DATABASE ) . orElseThrow ( ( ) - > missingFieldException ( CONFIG_DATABASE ) ) ;
stockDb = new SqliteDb ( connect ( dbFile ) ) ;
@ -46,15 +46,16 @@ public class StockApi extends BaseHandler implements StockService {
@@ -46,15 +46,16 @@ public class StockApi extends BaseHandler implements StockService {
if ( user . isEmpty ( ) ) return unauthorized ( ex ) ;
var head = path . pop ( ) ;
return switch ( head ) {
case ITEMS_AT - > {
case LOCATION - > {
try {
var id = Long . parseLong ( path . pop ( ) ) ;
yield getItemsAt ( user . get ( ) , id , ex ) ;
yield getLocation ( user . get ( ) , id , ex ) ;
} catch ( Exception e ) {
yield super . doGet ( path , ex ) ;
}
}
case LOCATIONS - > getLocations ( path , user . get ( ) , ex ) ;
case PROPERTIES - > getProperties ( ex ) ;
case null , default - > super . doGet ( path , ex ) ;
} ;
} catch ( UmbrellaException e ) {
@ -62,7 +63,30 @@ public class StockApi extends BaseHandler implements StockService {
@@ -62,7 +63,30 @@ public class StockApi extends BaseHandler implements StockService {
}
}
private boolean getItemsAt ( UmbrellaUser user , long locationId , HttpExchange ex ) throws IOException {
@Override
public boolean doPost ( Path path , HttpExchange ex ) throws IOException {
addCors ( ex ) ;
try {
Optional < Token > token = SessionToken . from ( ex ) . map ( Token : : of ) ;
var user = userService ( ) . loadUser ( token ) ;
if ( user . isEmpty ( ) ) return unauthorized ( ex ) ;
var head = path . pop ( ) ;
return switch ( head ) {
case PROPERTY - > postProperty ( user . get ( ) , ex ) ;
case null , default - > super . doPost ( path , ex ) ;
} ;
} catch ( UmbrellaException e ) {
return send ( ex , e ) ;
}
}
private boolean getChildLocations ( UmbrellaUser user , long parentId , HttpExchange ex ) throws IOException {
LOG . log ( WARNING , "No security check implemented for {0}.getChildLocations(user, parentId, ex)!" , getClass ( ) . getSimpleName ( ) ) ; // TODO check, that user is allowed to request that location
return sendContent ( ex , stockDb . listChildLocations ( parentId ) . stream ( ) . sorted ( comparing ( l - > l . name ( ) . toLowerCase ( ) ) ) . map ( Location : : toMap ) ) ;
}
private boolean getLocation ( UmbrellaUser user , long locationId , HttpExchange ex ) throws IOException {
return sendContent ( ex , stockDb . listItemsAt ( locationId ) . stream ( ) . map ( Item : : toMap ) . toList ( ) ) ;
}
@ -83,10 +107,8 @@ public class StockApi extends BaseHandler implements StockService {
@@ -83,10 +107,8 @@ public class StockApi extends BaseHandler implements StockService {
} ;
}
private boolean getChildLocations ( UmbrellaUser user , long parentId , HttpExchange ex ) throws IOException {
LOG . log ( WARNING , "No security check implemented for {0}.getChildLocations(user, parentId, ex)!" , getClass ( ) . getSimpleName ( ) ) ; // TODO check, that user is allowed to request that location
return sendContent ( ex , stockDb . listChildLocations ( parentId ) . stream ( ) . sorted ( comparing ( l - > l . name ( ) . toLowerCase ( ) ) ) . map ( Location : : toMap ) ) ;
private boolean getProperties ( HttpExchange ex ) throws IOException {
return sendContent ( ex , stockDb . listProperties ( ) . stream ( ) . map ( Property : : toMap ) . toList ( ) ) ;
}
private boolean getUserLocations ( UmbrellaUser user , HttpExchange ex ) throws IOException {
@ -109,6 +131,35 @@ public class StockApi extends BaseHandler implements StockService {
@@ -109,6 +131,35 @@ public class StockApi extends BaseHandler implements StockService {
return sendContent ( ex , result ) ;
}
private boolean postProperty ( UmbrellaUser user , HttpExchange ex ) throws IOException {
var json = json ( ex ) ;
if ( ! ( json . get ( ID ) instanceof Number id ) ) throw missingFieldException ( ID ) ;
if ( ! ( json . get ( FIELD_ITEM ) instanceof JSONObject itemData ) ) throw missingFieldException ( FIELD_ITEM ) ;
if ( ! ( itemData . get ( OWNER ) instanceof JSONObject owner ) ) throw missingFieldException ( OWNER ) ;
if ( ! ( json . get ( "add_prop" ) instanceof JSONObject propData ) ) throw missingFieldException ( "add_prop" ) ;
if ( ! propData . has ( VALUE ) ) throw missingFieldException ( VALUE ) ;
var value = propData . get ( VALUE ) ;
if ( value = = null ) throw missingFieldException ( VALUE ) ;
var keys = owner . keySet ( ) ;
if ( keys . size ( ) ! = 1 ) throw unprocessable ( "{0} expected to have only one child!" , OWNER ) ;
var key = new ArrayList < > ( keys ) . getFirst ( ) ;
var ownerId = switch ( key ) {
case COMPANY - > - json . getLong ( key ) ;
case USER - > json . getLong ( key ) ;
case null , default - > throw invalidFieldException ( format ( "Single child of {0}" , OWNER ) , format ( "either {0} or {1}" , COMPANY , USER ) ) ;
} ;
if ( propData . get ( "existing_prop_id" ) instanceof Number existingPropId & & existingPropId . longValue ( ) ! = 0L ) {
stockDb . addProperty ( ownerId , id . longValue ( ) , existingPropId . longValue ( ) , value ) ;
} else {
if ( ! ( propData . get ( "new_prop" ) instanceof JSONObject newProp ) ) throw unprocessable ( "data must contain either add_prop.existing_prop_id or add_prop.new_prop!" ) ;
if ( ! ( newProp . get ( NAME ) instanceof String name ) | | name . isBlank ( ) ) throw unprocessable ( "data.add_prop.new_prop does not contain name!" ) ;
var unit = newProp . get ( UNIT ) instanceof String u ? nullIfEmpty ( u ) : null ;
stockDb . addProperty ( ownerId , id . longValue ( ) , name , value , unit ) ;
}
return sendEmptyResponse ( 500 , ex ) ;
}
@Override
public Collection < Object > redefineMe ( long company_id ) {
return List . of ( ) ;