This SDK provides a simple interface to features offered by the various services of HSDP.
The output of the project is a JAR-file that can be used in either a Java or Kotlin backend service or an Android application.
- Using in your projects
- Basic usage
- Supported APIs
- Logging implementation examples
- Todo
- Issues
- Contact / Getting help
- License
The library dependency can be included in your projects in different ways.
repositories {
// Other repos...
maven { setUrl("https://jitpack.io") }
}
dependencies {
// Other dependencies...
implementation("com.github.philips-software:kotlin-hsdp-sdk:0.2.0")
}
repositories {
// Other repos...
maven { url 'https://jitpack.io' }
}
dependencies {
// Other dependencies...
implementation "com.github.philips-software:kotlin-hsdp-sdk:0.2.0"
}
<repositories>
...
<repository>
<id>jitpack.io</id>
<url>https://jitpack.io</url>
</repository>
</repositories>
<dependencies>
...
<dependency>
<groupId>com.github.philips-software</groupId>
<artifactId>kotlin-hsdp-sdk</artifactId>
<version>0.2.0</version>
</dependency>
</dependencies>
Simple examples for Android and backend (Kotlin & Java) applications are provided to give you a quick start.
The library externalized the choice of a logging system by providing an abstract logger factory and a logger interface. It is up to the application developer to provide implementations of both, and to register the concrete logger factory to the logger manager before instantiating any SDK services.
Next code example shows how to use the library in an Android project written in Kotlin.
class MainActivity : AppCompatActivity() {
private val scope = CoroutineScope(Dispatchers.IO)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// First register the concrete logger factory, before creating any of the services!
// This will allow (future) versions of the SDK services to also log something in their construction phase.
PlatformLoggerFactory.registerConcreteFactory(TimberLoggerFactory)
logger = PlatformLoggerFactory.create("Main", javaClass)
val httpClient = HttpClient()
val iamOAuth2 = IamOAuth2(iamUrl, basicAuthUsername, basicAuthPassword, httpClient, initialToken)
val tdr = TDR(tdrUrl, httpClient)
scope.launch {
try {
if (iamOAuth2.token.isNotValid) {
logger.error { "no valid token" }
val token = iamOAuth2.login(username, password)
}
val contracts = tdr.getContracts(
ContractQuery(
organizationQuery = OrganizationQuery(tenant),
countQuery = CountQuery(10)
)
)
logger.info { "Getting contracts for $tenant returned page with ${contracts.data.size} items" }
logger.info { "revoke token" }
val revokedToken = iamOAuth2.revokeToken()
} catch (ex: IOException) {
logger.error { ex.toString() }
} catch (httpEx: HttpException) {
logger.error { httpEx.toString() }
} catch (serializationException: SerializationException) {
logger.error { serializationException.toString() }
}
}
}
}
Notes:
- The example registers a
TimberLoggerFactory
to thePlatformLoggerFactory
. Former will be detailed in the logging section. iamUrl
,basicAuthUsername
,basicAuthPassword
,tdrUrl
,tenant
,username
andpassword
are somehow provided by the application.initialToken
can be provided, to allow the application to store the last obtained token somewhere safely, so that upon reopening of the application there is no need for logging in again when the token is still valid.
Next code example shows how to use the library in a Kotlin backend project.
import some.sdk.packages
fun main() {
// First register the concrete logger factory, before creating any of the services!
// This will allow (future) versions of the SDK services to also log something in their construction phase.
PlatformLoggerFactory.registerConcreteFactory(Log4j2LoggerFactory)
val httpClient = HttpClient()
val iamOAuth2 = IamOAuth2(iamUrl, basicAuthUsername, basicAuthPassword, httpClient)
val tdr = TDR(tdrUrl, httpClient)
runBlocking {
iamOAuth2.login(username, password)
val dataItems = tdr.getDataItems(
query = DataItemQuery(organizationQuery = OrganizationQuery(tenant))
)
println(dataItems)
iamOAuth2.revokeToken()
}
}
Notes:
- The example registers a
Log4j2LoggerFactory
to thePlatformLoggerFactory
. Former will be detailed in the logging section. iamUrl
,basicAuthUsername
,basicAuthPassword
,tdrUrl
,tenant
,username
andpassword
are somehow provided by the application.
As mentioned in the design decisions, the Java application must instantiate the service proxies instead of the services themselves.
Next code example shows how to use the library in a Java Spring Boot backend project.
@SpringBootApplication
public class SimpleApplication {
public static void main(String[] args) {
PlatformLoggerFactory.registerConcreteFactory(new SimpleLoggerFactory());
SpringApplication.run(SimpleApplication.class, args);
}
@Bean
HttpClient getHttpClient() {
return new HttpClient();
}
@Bean
IamOAuth2JavaProxy getIamOAuth2() {
return new IamOAuth2JavaProxy(
iamUrl,
basicAuthUsername,
basicAuthPassword,
getHttpClient()
);
}
@Bean
TDRJavaProxy getTDR() {
return new TDRJavaProxy(
tdrUrl,
getHttpClient()
);
}
}
An example REST controller that performs calls to the SDK services for IAM and TDR:
@RestController
@RequestMapping("/tdr")
class TdrController {
private final HttpClient httpClient;
private final IamOAuth2JavaProxy iamOAuth2;
private final TDRJavaProxy tdr;
public TdrController(HttpClient httpClient, IamOAuth2JavaProxy iamOAuth2, TDRJavaProxy tdr) {
this.httpClient = httpClient;
this.iamOAuth2 = iamOAuth2;
this.tdr = tdr;
}
@GetMapping("contracts")
public CompletableFuture<ContractsDto> getContracts() {
return ascertainToken()
.thenCompose(s -> {
ContractQuery contractQuery = new ContractQueryBuilder()
.organizationQuery(new OrganizationQuery(tenant))
.countQuery(new CountQuery(10))
.offsetQuery(new OffsetQuery(0))
.build();
return tdr.getContracts(contractQuery);
});
}
private CompletableFuture<Void> ascertainToken() {
if (!httpClient.getToken().isValid()) {
return iamOAuth2.login(username, password)
.thenAccept(token -> System.out.println(token.getAccessToken()));
}
return CompletableFuture.completedFuture(null);
}
}
Notes:
- The example registers a
SimpleLoggerFactory
to thePlatformLoggerFactory
. Former will be detailed in the logging section. iamUrl
,basicAuthUsername
,basicAuthPassword
,tdrUrl
,tenant
,username
andpassword
are somehow provided by the application.
The current implementation covers only a subset of HSDP APIs. Additional functionality is built as needed.
- IAM Identity and Access Management (IAM)
- Access Management
- Federation
- OAuth2
- OAuth2 Authorization
- OAuth2 Token Revocation
- OpenID Connect UserInfo
- Introspect
- Session (refresh, terminate)
- OpenID (configuration, JWKS)
- Token
- Identity Management
- User
- Search user account
- Register a user account
- Delete a user account
- Set password for user account
- Change user password
- Reset password service with kba validation
- Enables user password to be reset by admin
- Resend account activation email to the user
- Unlock user account
- Retrieve saved kba challenges questions for a user
- Get effective password policy for a user
- Send verification code (OTP) to secondary auth factors like email and SMS
- Verify the code (OTP) sent to secondary auth factors like email and SMS
- Remove OTP device registration
- Update user's login ID
- Enables a user to delegate access to another user to act on its behalf
- Revoke delegation granted to given delegatee
- User
- Policy Management
- Access Management
- Clinical Data Repository (CDR)
- Read
- VRead (versioned read)
- Create
- Update
- Patch
- Delete
- Get History
- Batch operation
- Get Capabilities
- Telemetry Data Repository (TDR)
- Contracts
- Data Items
- Provisioning
- Provisioning
- Provision
- Create Identity
- Reprovision
- Unprovisioning
- Reset
- Identity Certificate
- Task
- Provisioning
Other services will follow later.
A possible implementation for logging with Timber:
object TimberLoggerFactory: AbstractPlatformLoggerFactory {
init {
Timber.plant(Timber.DebugTree())
}
override fun create(tag: String, ofClass: Class<*>): PlatformLogger = TimberLogger(tag)
}
class TimberLogger(private val tag: String): PlatformLogger {
private val traceLevel = 1
override fun fatal(message: () -> String) {
Timber.tag(tag).wtf(message())
}
override fun error(message: () ->String) {
Timber.tag(tag).e(message())
}
override fun warn(message: () -> String) {
Timber.tag(tag).w(message())
}
override fun info(message: () -> String) {
Timber.tag(tag).i(message())
}
override fun debug(message: () -> String) {
Timber.tag(tag).d(message())
}
override fun trace(message: () -> String) {
Timber.tag(tag).log(traceLevel, message())
}
}
A possible implementation for logging with Log4j2:
import com.philips.hsdp.apis.support.logging.AbstractPlatformLoggerFactory
import com.philips.hsdp.apis.support.logging.PlatformLogger
import org.apache.logging.log4j.kotlin.loggerOf
object Log4j2LoggerFactory: AbstractPlatformLoggerFactory {
override fun create(tag: String, ofClass: Class<*>): PlatformLogger = Log4j2Logger(tag, ofClass)
}
class Log4j2Logger(private val tag: String, ofClass: Class<*>) : PlatformLogger {
private val logger = loggerOf(ofClass)
override fun fatal(message: () -> String) {
logger.fatal(message)
}
override fun error(message: () -> String) {
logger.error(message)
}
override fun warn(message: () -> String) {
logger.warn(message)
}
override fun info(message: () -> String) {
logger.info(message)
}
override fun debug(message: () -> String) {
logger.debug(message)
}
override fun trace(message: () -> String) {
logger.trace(message)
}
}
A possible implementation for logging with just println:
public class SimpleLoggerFactory implements AbstractPlatformLoggerFactory {
@NotNull
@Override
public PlatformLogger create(@NotNull String tag, @NotNull Class<?> ofClass) {
return new SimpleLogger(tag, ofClass);
}
}
class SimpleLogger implements PlatformLogger {
private final String tag;
private final Class<?> ofClass;
public SimpleLogger(String tag, Class<?> ofClass) {
this.tag = tag;
this.ofClass = ofClass;
}
@Override
public void debug(@NotNull Function0<String> message) {
System.out.println(buildMessage("DEBUG", message));
}
@Override
public void error(@NotNull Function0<String> message) {
System.out.println(buildMessage("ERROR", message));
}
@Override
public void fatal(@NotNull Function0<String> message) {
System.out.println(buildMessage("FATAL", message));
}
@Override
public void info(@NotNull Function0<String> message) {
System.out.println(buildMessage("INFO", message));
}
@Override
public void trace(@NotNull Function0<String> message) {
System.out.println(buildMessage("TRACE", message));
}
@Override
public void warn(@NotNull Function0<String> message) {
System.out.println(buildMessage("WARN", message));
}
private String buildMessage(String level, Function0<String> message) {
return level + " " + ofClass.getSimpleName() + " " + message.invoke();
}
}
- Implement more HSDP API calls
- If you have an issue: report it on the issue tracker
Aad Rijnberg (aad.rijnberg@philips.com)
Meindert Schuitema (meindert.schuitema@philips.com)
Martijn van Welie (martijn.van.welie@philips.com)
See LICENSE.md.