Step 5: Add receipt.png#

You can use the Java Client Libraries to save non-RDF resources (e.g., .png, .pdf) to your Pod.

For this part of the tutorial, the getting started app uses Inrupt’s Java client library to:

  • Store a copy of the expense receipt(s) to your Pod.

  • Update the associated expense to link to the URL(s) of the receipt(s).

Tip

You can find the complete code at Complete Code.

Update application.properties#

Add the following properties to the src/main/resources/application.properties file:

spring.servlet.multipart.max-file-size=128KB
spring.servlet.multipart.max-request-size=128KB

Modify Expense Class#

  1. Open Expense class file:

    Open src/main/java/com/example/gettingstarted/Expense.java.

  2. Add the java.util.* import statement:

    import java.util.*;
    
  3. Add the predicate definition for the receipts in the Expense class:

    static IRI SCHEMA_IMAGE = rdf.createIRI("https://schema.org/image");
    
  4. Update the Expense class constructor to include the receipts:

    @JsonCreator
    public Expense(@JsonProperty("identifier") final URI identifier,
                   @JsonProperty("merchantProvider") String merchantProvider,
                   @JsonProperty("expenseDate") Date expenseDate,
                   @JsonProperty("description") String description,
                   @JsonProperty("amount") BigDecimal amount,
                   @JsonProperty("currency") String currency,
                   @JsonProperty("category") String category,
                   @JsonProperty("receipts") String[] receipts) {
        this(identifier);
        this.setRdfType(MY_RDF_TYPE_VALUE);
        this.setMerchantProvider(merchantProvider);
        this.setExpenseDate(expenseDate);
        this.setDescription(description);
        this.setAmount(amount);
        this.setCurrency(currency);
        this.setCategory(category);
        this.setReceipts(receipts);
    }
    
  5. Add the getter and setters for the receipts in the Expense class:

    public Set<String> getReceipts() {
        return subject.getReceipts();
    }
    // Note:: The setters first uses the getter, which returns a Set, and adds the receipt to the set.
    public void addReceipt(String receipt) {
        subject.getReceipts().add(receipt);
    }
    public void setReceipts(String[] receipts) {
        subject.getReceipts().addAll(List.of(receipts));
    }
    
  6. Add getter for the receipts in the inner Node class:

    public Set<String> getReceipts() {
        return objects(SCHEMA_IMAGE, TermMappings::asIri, ValueMappings::iriAsString);
    }
    

Note

For more detail with regards to handling lists, see Data Modeling (RDF).

Modify ExpenseController Class#

  1. Open ExpenseController class file.

    Open src/main/java/com/example/gettingstarted/ExpenseController.java.

  2. Add the following import statement:

    import org.springframework.web.multipart.MultipartFile;
    
  3. Add the following method to handle the upload of non-RDF files:

    /**
     * Note 9: Stores a non-RDF resource to a Pod
     *
     * - Build a PUT request using Request.newBuilder()...build();
     * - Send the request using SolidSyncClient's low-level client.send() method;
     */
    @PutMapping("/resource/nonRDF/add")
    public boolean addNonRDFFile(@RequestParam(value = "destinationURL") String destinationURL,
                                 @RequestParam(value = "file") MultipartFile file) {
        printWriter.println("In addNonRDFFile:: Save Non-RDF File to Pod.");
        try (final var fileStream = file.getInputStream()) {
            Request request = Request.newBuilder()
                    .uri(URI.create(destinationURL))
                    .header("Content-Type", file.getContentType())
                    .PUT(Request.BodyPublishers.ofInputStream(fileStream))
                    .build();
            Response<Void> response = client.send(
                    request,
                    Response.BodyHandlers.discarding());
            if (response.statusCode() == 201 || response.statusCode() == 200 || response.statusCode() == 204)
                return true;
        } catch (java.io.IOException e1) {
            e1.printStackTrace();
        }
    
        return false;
    }
    
  4. Add the following method that uploads a receipt file and links it to the associated Expense.

    /**
     * Note 10: Stores a non-RDF resource (image of the receipt) to a Pod and Attach to an Expense
     * Using methods defined as part of getting started, addReceiptToExpense:
     * - Calls addNonRDFFile() to store the receipt to a Pod
     * - Calls getExpense() to fetch the associated Expense RDF resource.
     * - Calls the Expense's setter `addReceipt` to add the link to the saved receipt.
     * - Calls updateExpense() to save the updated Expense.
     */
    @PutMapping("/expenses/receipts/add")
    public String addReceiptToExpense(@RequestParam(value = "destinationURL") String destinationURL,
                                      @RequestParam(value = "file") MultipartFile file,
                                      @RequestParam(value = "expenseURL") String expenseURL) {
        printWriter.println("In addReceiptToExpense: Save Receipt File to Pod and Update Associated Expense.");
        boolean success = addNonRDFFile(destinationURL, file);
        if (success) {
            Expense expense = getExpense(expenseURL);
            expense.addReceipt(destinationURL);
            return updateExpense(expense);
        } else {
            printWriter.println("Error adding receipt");
            return null;
        }
    }