Step 3: ExpenseController Class#
For the WebService in this tutorial, the ExpenseController
defines
the endpoints for the various Read and Write (CRUD) operations.
Create the ExpenseController
Class#
Tip
Various aspects related to CRUD operations with the Inrupt Client Libraries are noted as comments in the code. For more details, see CRUD.
In the src/main/java/com/example/gettingstarted/
directory,
create ExpenseController.java
file with the content below:
package com.example.gettingstarted;
import com.inrupt.client.auth.Session;
import com.inrupt.client.openid.OpenIdSession;
import com.inrupt.client.solid.SolidSyncClient;
import com.inrupt.client.webid.WebIdProfile;
import com.inrupt.client.solid.PreconditionFailedException;
import com.inrupt.client.solid.ForbiddenException;
import com.inrupt.client.solid.NotFoundException;
import org.springframework.web.bind.annotation.*;
import org.apache.commons.rdf.api.RDFSyntax;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.net.URI;
import java.util.Set;
@RequestMapping("/api")
@RestController
public class ExpenseController {
/**
* Note 1: Authenticated Session
* Using the client credentials, create an authenticated session.
*/
final Session session = OpenIdSession.ofClientCredentials(
URI.create(System.getenv("MY_SOLID_IDP")),
System.getenv("MY_SOLID_CLIENT_ID"),
System.getenv("MY_SOLID_CLIENT_SECRET"),
System.getenv("MY_AUTH_FLOW"));
/**
* Note 2: SolidSyncClient
* Instantiates a synchronous client for the authenticated session.
* The client has methods to perform CRUD operations.
*/
final SolidSyncClient client = SolidSyncClient.getClient().session(session);
private final PrintWriter printWriter = new PrintWriter(System.out, true);
/**
* Note 3: SolidSyncClient.read()
* Using the SolidSyncClient client.read() method, reads the user's WebID Profile document and returns the Pod URI(s).
*/
@GetMapping("/pods")
public Set<URI> getPods(@RequestParam(value = "webid", defaultValue = "") String webID) {
printWriter.println("ExpenseController:: getPods");
try (final var profile = client.read(URI.create(webID), WebIdProfile.class)) {
return profile.getStorages();
}
}
/**
* Note 4: SolidSyncClient.create()
* Using the SolidSyncClient client.create() method,
* - Saves the Expense as an RDF resource to the location specified in the Expense.identifier field.
*/
@PostMapping(path = "/expenses/create")
public Expense createExpense(@RequestBody Expense newExpense) {
printWriter.println("ExpenseController:: createExpense");
try (var createdExpense = client.create(newExpense)) {
printExpenseAsTurtle(createdExpense);
return createdExpense;
} catch(PreconditionFailedException e1) {
// Errors if the resource already exists
printWriter.println(String.format("[%s] com.inrupt.client.solid.PreconditionFailedException:: %s", e1.getStatusCode(), e1.getMessage()));
} catch(ForbiddenException e2) {
// Errors if user does not have access to create
printWriter.println(String.format("[%s] com.inrupt.client.solid.ForbiddenException:: %s", e2.getStatusCode(), e2.getMessage()));
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Note 5: SolidSyncClient.read()
* Using the SolidSyncClient client.read() method,
* - Reads the RDF resource into the Expense class.
*/
@GetMapping("/expenses/get")
public Expense getExpense(@RequestParam(value = "resourceURL", defaultValue = "") String resourceURL) {
printWriter.println("ExpenseController:: getExpense");
try (var resource = client.read(URI.create(resourceURL), Expense.class)) {
return resource;
} catch (NotFoundException e1) {
// Errors if resource is not found
printWriter.println(String.format("[%s] com.inrupt.client.solid.NotFoundException:: %s", e1.getStatusCode(), e1.getMessage()));
} catch(ForbiddenException e2) {
// Errors if user does not have access to read
printWriter.println(String.format("[%s] com.inrupt.client.solid.ForbiddenException:: %s", e2.getStatusCode(), e2.getMessage()));
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Note 6: SolidSyncClient.update()
* Using the SolidSyncClient client.update() method,
* - Updates the Expense resource.
*/
@PutMapping("/expenses/update")
public Expense updateExpense(@RequestBody Expense expense) {
printWriter.println("ExpenseController:: updateExpense");
try(var updatedExpense = client.update(expense)) {
printExpenseAsTurtle(updatedExpense);
return updatedExpense;
} catch (NotFoundException e1) {
// Errors if resource is not found
printWriter.println(String.format("[%s] com.inrupt.client.solid.NotFoundException:: %s", e1.getStatusCode(), e1.getMessage()));
} catch(ForbiddenException e2) {
// Errors if user does not have access to read
printWriter.println(String.format("[%s] com.inrupt.client.solid.ForbiddenException:: %s", e2.getStatusCode(), e2.getMessage()));
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
/**
* Note 7: SolidSyncClient.delete()
* Using the SolidSyncClient client.delete() method,
* - Deletes the resource located at the resourceURL.
*/
@DeleteMapping("/expenses/delete")
public void deleteExpense(@RequestParam(value = "resourceURL") String resourceURL) {
printWriter.println("ExpenseController:: deleteExpense");
try {
client.delete(URI.create(resourceURL));
// Alternatively, you can specify an Expense object to the delete method.
// The delete method deletes the Expense recorde located in the Expense.identifier field.
// For example: client.delete(new Expense(URI.create(resourceURL)));
} catch (NotFoundException e1) {
// Errors if resource is not found
printWriter.println(String.format("[%s] com.inrupt.client.solid.NotFoundException:: %s", e1.getStatusCode(), e1.getMessage()));
} catch(ForbiddenException e2) {
// Errors if user does not have access to read
printWriter.println(String.format("[%s] com.inrupt.client.solid.ForbiddenException:: %s", e2.getStatusCode(), e2.getMessage()));
} catch(Exception e) {
e.printStackTrace();
}
}
/**
* Note 8: Prints the expense resource in Turtle.
*/
private void printExpenseAsTurtle(Expense expense) {
printWriter.println("ExpenseController:: printExpenseAsTurtle");
ByteArrayOutputStream content = new ByteArrayOutputStream();
try {
expense.serialize(RDFSyntax.TURTLE, content);
printWriter.println(content.toString("UTF-8"));
} catch (IOException e) {
e.printStackTrace();
}
}
}
In the src/main/kotlin/com/example/gettingstarted/
directory, create an ExpenseController.kt
Kotlin class with the
following content:
package com.example.gettingstarted
import com.inrupt.client.openid.OpenIdSession
import com.inrupt.client.solid.SolidSyncClient
import com.inrupt.client.webid.WebIdProfile
import com.inrupt.client.solid.PreconditionFailedException
import com.inrupt.client.solid.ForbiddenException
import com.inrupt.client.solid.NotFoundException
import org.springframework.web.bind.annotation.*
import java.io.PrintWriter
import java.net.URI
import org.apache.commons.rdf.api.RDFSyntax
import java.io.ByteArrayOutputStream
import java.io.IOException
@RequestMapping("/api")
@RestController
class ExpenseController {
/**
* Note 1: Authenticated Session
* Using the client credentials, create an authenticated session.
*/
val session = OpenIdSession.ofClientCredentials(
URI.create(System.getenv("MY_SOLID_IDP")),
System.getenv("MY_SOLID_CLIENT_ID"),
System.getenv("MY_SOLID_CLIENT_SECRET"),
System.getenv("MY_AUTH_FLOW")
)
/**
* Note 2: SolidSyncClient
* Instantiates a synchronous client for the authenticated session.
* The client has methods to perform CRUD operations.
*/
val client = SolidSyncClient.getClient().session(session)
private val printWriter = PrintWriter(System.out, true)
/**
* Note 3: SolidSyncClient.read()
* Using the SolidSyncClient client.read() method, reads the user's WebID Profile document and returns the Pod URI(s).
*/
@GetMapping("/pods")
fun getPods(@RequestParam(value = "webid", defaultValue = "") webID: String): Set<URI> {
printWriter.println("ExpenseController:: getPods")
client.read(URI.create(webID), WebIdProfile::class.java).use { profile -> return profile.storages }
}
/**
* Note 4: SolidSyncClient.create()
* Using the SolidSyncClient client.create() method,
* - Saves the Expense as an RDF resource to the location specified in the Expense.identifier field.
*/
@PostMapping("/expenses/create")
fun createExpense(@RequestBody newExpense: Expense): Expense? {
printWriter.println("ExpenseController:: createExpense")
try {
val createdExpense = client.create(newExpense)
printExpenseAsTurtle(createdExpense)
return createdExpense
} catch(e1: PreconditionFailedException) {
// Errors if the resource already exists
printWriter.println(String.format("[%s] com.inrupt.client.solid.PreconditionFailedException:: %s", e1.statusCode, e1.localizedMessage))
} catch(e2: ForbiddenException) {
// Errors if user does not have access to create
printWriter.println(String.format("[%s] com.inrupt.client.solid.ForbiddenException:: %s", e2.statusCode, e2.localizedMessage))
} catch(e: Exception) {
e.printStackTrace()
}
return null
}
/**
* Note 5: SolidSyncClient.read()
* Using the SolidSyncClient client.read() method,
* - Reads the RDF resource into the Expense class.
*/
@GetMapping("/expenses/get")
fun getExpense(
@RequestParam(
value = "resourceURL", defaultValue = ""
) resourceURL: String
): Expense? {
printWriter.println("ExpenseController:: getExpense")
try {
client.read(
URI.create(resourceURL), Expense::class.java
).use { resource -> return resource }
} catch(e1: NotFoundException) {
// Errors if the resource is not found
printWriter.println(String.format("[%s] com.inrupt.client.solid.NotFoundException:: %s", e1.statusCode, e1.localizedMessage))
} catch(e2: ForbiddenException) {
// Errors if user does not have access to read
printWriter.println(String.format("[%s] com.inrupt.client.solid.ForbiddenException:: %s", e2.statusCode, e2.localizedMessage))
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
/**
* Note 6: SolidSyncClient.update()
* Using the SolidSyncClient client.update() method,
* - Updates the Expense resource.
*/
@PutMapping("/expenses/update")
fun updateExpense(@RequestBody expense: Expense): Expense? {
printWriter.println("ExpenseController:: updateExpense")
try {
val updatedExpense = client.update(expense)
printExpenseAsTurtle(updatedExpense)
return updatedExpense
} catch(e1: NotFoundException) {
// Errors if the resource is not found
printWriter.println(String.format("[%s] com.inrupt.client.solid.NotFoundException:: %s", e1.statusCode, e1.localizedMessage))
} catch(e2: ForbiddenException) {
// Errors if user does not have access to read
printWriter.println(String.format("[%s] com.inrupt.client.solid.ForbiddenException:: %s", e2.statusCode, e2.localizedMessage))
} catch (e: Exception) {
e.printStackTrace()
}
return null
}
/**
* Note 7: SolidSyncClient.delete()
* Using the SolidSyncClient client.delete() method,
* - Deletes the resource located at the resourceURL.
*/
@DeleteMapping("/expenses/delete")
fun deleteExpense(@RequestParam(value = "resourceURL") resourceURL: String) {
printWriter.println("ExpenseController:: deleteExpense")
try {
client.delete(URI.create(resourceURL))
// Alternatively, you can specify an Expense object to the delete method.
// The delete method deletes the Expense recorde located in the Expense.identifier field.
// For example: client.delete(Expense(URI.create(resourceURL)))
} catch(e1: NotFoundException) {
// Errors if the resource is not found
printWriter.println(String.format("[%s] com.inrupt.client.solid.NotFoundException:: %s", e1.statusCode, e1.localizedMessage))
} catch(e2: ForbiddenException) {
// Errors if user does not have access to read
printWriter.println(String.format("[%s] com.inrupt.client.solid.ForbiddenException:: %s", e2.statusCode, e2.localizedMessage))
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* Note 8: Prints the expense resource in Turtle.
*/
fun printExpenseAsTurtle(
expense: Expense
) {
printWriter.println("ExpenseController:: printExpenseAsTurtle")
val content = ByteArrayOutputStream()
try {
expense.serialize(RDFSyntax.TURTLE, content)
printWriter.println(content.toString("UTF-8"))
} catch (e: IOException) {
e.printStackTrace()
}
}
}