Skip to content

Tutorial 02 17 Adding Patch Endpoints

mattl91 edited this page Mar 3, 2023 · 11 revisions

Harmony Core Logo

Adding Patch Endpoints

The term "patch" is used to describe an operation where an entity is partially updated.

VIDEO: Creating a Basic Solution

In Harmony Core, as in many other RESTful web service implementations, an HTTP PATCH operation is used to perform a patch operation for an entity.

When performing an HTTP PATCH, there are several requirements that the client must comply with in order for an entity to be patched:

  • The body of the HTTP request must include a valid JSON patch document that describes one or more changes that are to be applied to an existing entity.

  • The HTTP request must include a header named Content-Type that identifies the type of data being sent in the request body as being application/json.

  • The HTTP request must include a header named Content-Length that specifies the length of the data (in bytes) being sent in the request body. Note that some client tools, including Postman, will automatically add this header based on the data you pass.

Enabling Patch Endpoints

To generate endpoints that allow clients to patch entities via HTTP PATCH operations, you must set the Enable PATCH endpoints option in the Harmony Core CLI tool:

  1. At a Windows prompt, move to the directory with your Harmony Core solution, and type harmonycore gui. Then, when the GUI for the Harmony Core CLI tool opens, go to the OData screen, scroll down to the Enable PATCH endpoints option, and double-click it.

  2. In the "Enter new value" screen, click the diamond icon to the right of Enable PATCH endpoints. (The diamond will change to a checkmark.) Then click OK.

Generating the Code

  1. Save the settings you made by selecting File > Save from the menu.

  2. Generate code for your solution by selecting Codegen > Regen from the menu. When code generation is finished, a message lists the files that were generated. Click OK to close the message.

What Changed

Setting the Enable PATCH endpoints option causes an additional endpoint method to be added to each of the generated OData controller classes. The new method (with most of the code removed from the procedure division) looks like this:

        {HttpPatch("Customers(CustomerNumber={aCustomerNumber})")}
        {Produces("application/json")}
        {ProducesResponseType(StatusCodes.Status204NoContent)}
        {ProducesResponseType(StatusCodes.Status400BadRequest)}
        {ProducesResponseType(StatusCodes.Status404NotFound)}
        ;;; <summary>
        ;;; Patch  (partial update) a customer.
        ;;; </summary>
        ;;; <param name="aCustomerNumber">Customer number</param>
        ;;; <returns>Returns an IActionResult indicating the status of the operation and containing any data that was returned.</returns>
        public method PatchCustomer, @IActionResult
            {FromODataUri}
            required in aCustomerNumber, int
            {FromBody}
            required in aCustomer, @JsonPatchDocument<Customer>
        proc
            ;; Validate inbound data
            if (!ModelState.IsValid)
                mreturn ValidationHelper.ReturnValidationError(ModelState)



    mreturn NoContent()

endmethod

This sample code was taken from CustomersController.dbl, and as you can see, the code accepts two parameters:

  • A parameter named aCustomerNumber, which is the primary key value for the entity to be patched. Notice that this parameter is decorated with an attribute, {FromODataUri}, which indicates that the data must be provided via a URL parameter in the HTTP request.

  • A parameter named aCustomer, which is a JSON patch document of type Customer, containing the change instructions that are to be applied to the existing customer entity. Notice that this parameter is decorated with an attribute, {FromBody}, which indicates that the patch document must be provided via the body of the HTTP request.

The little bit of code shown in the procedure division in the example above does two things:

  • It checks the value of ModelState.IsValid and returns an error if it's false. Code that has already been executed in the ASP.NET Core pipeline has already inspected the inbound data from the client, and if that data is determined to be invalid for any reason, ModelState.IsValid is set to false. By returning ValidationHelper.ReturnValidationError(ModelState), we ensure that the client will receive an HTTP 400 (bad request) response, and also that information about the invalid data will be included in the body of the response to the client.

  • On successful completion, a NoContent() response is returned. This will result in an HTTP 204 (no content) response to the client.

You will find similar new code in the other controllers.

If you are generating Postman tests, a new PATCH request is added to the folder for each entity type, but you will need to re-import the newly generated tests into Postman. The instructions will walk you through doing this later.

Building the Code

  1. In Visual Studio, select Build > Rebuild Solution from the menu.

  2. Check the Output window. You should see something like this:

    1>------ Rebuild All started: Project: Repository, Configuration: Debug Any CPU ------
    2>------ Rebuild All started: Project: Services.Models, Configuration: Debug Any CPU ------
    3>------ Rebuild All started: Project: Services.Controllers, Configuration: Debug Any CPU ------
    4>------ Rebuild All started: Project: Services.Isolated, Configuration: Debug Any CPU ------
    5>------ Rebuild All started: Project: Services, Configuration: Debug Any CPU ------
    6>------ Rebuild All started: Project: Services.Host, Configuration: Debug Any CPU ------
    ========== Rebuild All: 6 succeeded, 0 failed, 0 skipped ==========
    

Testing the New Functionality

  1. In Visual Studio, press F5 (Start Debugging) to start the self-hosting application. Once again, you should see the console window appear with the messages confirming that your service is running.

It is not possible to test the functionality of the new endpoints using a web browser because the functionality to be tested involves issuing an HTTP PATCH request. Our tool of choice for issuing PATCH requests is Postman.

  1. Start Postman and close any request tabs that may be open.

  2. Select File > Import from the Postman menu, or click the Import button.

  3. In the Import dialog, click the Choose Files button, which opens the Open dialog.

  4. Browse to your main solution folder, select the PostManTests.postman_collection.json file, and then click the Open button.

  5. On the Import Elements tab, click the Import button.

  6. In the COLLECTION EXISTS dialog, click the Replace button.

Postman will now reload the tests in the "Harmony Core Sample API" collection. Notice that the total number of tests increases.

  1. Open the Customer Tests folder and select the PATCH Patch customer request.

Notice the following:

  • The HTTP method is set to PATCH.

  • The URL is set to {{ServerBaseUri}}/{{ODataPath}}/v{{ApiVersion}}/Customers(CustomerNumber=123).

  • If you click on the Headers tab, you will see that the Content-Type header is set to application/json.

  • If you click on the Body tab, you will see that the request contains a sample JSON patch document:

    [
      {
        "op": "replace",
        "path": "PropertyName",
        "value": "PropertyValue"
      }
    ]
    
  1. Change the value of the CustomerNumber parameter in the URL to 1.

  2. In the request body, change the JSON patch document so that it looks like this:

[
  {
    "op": "replace",
    "path": "Street",
    "value": "123 Main St."
  },
  {
    "op": "replace",
    "path": "City",
    "value": "Roseville"
  },
  {
    "op": "replace",
    "path": "ZipCode",
    "value": "95747"
  }
]

Now let's examine the current data for customer 1.

  1. Select the GET Read customer request, change the value of the CustomerNumber parameter in the URL to 1, and click the blue Send button.

You should see that customer 1 currently looks like this:

{
    "@odata.context": "https://localhost:8086/odata/v1/$metadata#Customers/$entity",
    "CustomerNumber": 1,
    "Name": "San Pablo Nursery",
    "Street": "1324 San Pablo Dam Road",
    "City": "San Pablo",
    "State": "CA",
    "ZipCode": 94806,
    "Contact": "Karen Graham",
    "Phone": "(555) 912-2341",
    "Fax": "(555) 912-2342",
    "FavoriteItem": 13,
    "PaymentTermsCode": "01",
    "TaxId": 559244251,
    "CreditLimit": 2000.00,
    "GlobalRFA": "ACAAAAAA4xlSqQ=="
}
  1. Now switch back to the PATCH Patch customer request and click the blue Send button.

You should see a response of 204 No Content, indicating a successful PATCH.

  1. Once again, select the GET Read Customer request and click the blue Send button.

You should see that customer 1 now looks like this:

{
    "@odata.context": "https://localhost:8086/odata/v1/$metadata#Customers/$entity",
    "CustomerNumber": 1,
    "Name": "San Pablo Nursery",
    "Street": "123 Main St.",
    "City": "Roseville",
    "State": "CA",
    "ZipCode": 95747,
    "Contact": "Karen Graham",
    "Phone": "(555) 912-2341",
    "Fax": "(555) 912-2342",
    "FavoriteItem": 13,
    "PaymentTermsCode": "01",
    "TaxId": 559244251,
    "CreditLimit": 2000.00,
    "GlobalRFA": "ACAAAAAAttjUxw=="
}

Notice that the values of the Street, City, and ZipCode properties have been patched to the values you provided.

Stop the Service

  1. When you are done testing, stop the self-hosting application.

Suppressing Patch Endpoints

Enabling patch endpoints adds endpoints to all your code-generated OData controllers, but it is possible to prevent the generation of these endpoints for certain structures. This capability is documented in Structure-Specific Endpoint Control.


Next topic: Adding Delete Endpoints


Clone this wiki locally