Error handling for deletes causing constraint violations

Hello everyone,

Today I've been investigating OLMIS-1705 that talks about DELETE endpoints not working. The problem with the delete is that it may affect the not-null constraint in another entity, in which case the resulting SQL query will fail. This is wrapped into the DataIntegrityViolationException. As an example, attempting to delete a geographic zone will fail if there's any facility that is currently linked to that geographic zone (because Facility requires non-null geographic zone).

I've been wondering about the approach we want to take to handle those errors. What currently happens is that the delete throws DataIntegrityViolationException, which is translated into 500 error code. We could try to implement manual validations before the actual delete happens, but we've got quite a lot of the not-null constraints scattered across the services and having to check all of the instances of let's say 10 entities upon delete somehow doesn't seem like a good solution. Any ideas for other approaches to tackle this? Or should we just leave it be 500 error code?

Best regards,

Sebastian.

···


      Sebastian Brudziński

    Software Developer / Team Leader

SolDevelo Sp. z o. o. [LLC]

     Office:  +48 58 782 45 40 / Fax:  +48 58 782 45 41  Al. Zwycięstwa 96/98  81-451, Gdynia

     [http://www.soldevelo.com](http://www.SolDevelo.com)

               Place of registration: Regional Court for the City of Gdansk            KRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,            Share capital: 60,000.00 PLN

sbrudzinski@soldevelo.com

Hi Sebastian. I’m of the opinion that in general DELETE shouldn’t delete anything, but rather mark it as deleted/archived.

There are some simple positives:

  • we don’t lose data

  • we don’t have the referential integrity issue you described within a service

  • AND any client to a service that does a GET /resource/{id} will always return the resource once it’s created - that is it wouldn’t return a 200 on day 1, and on day 2 after it’s deleted suddenly return a 404 to the client. More on this later…

Some cons:

  • since we never delete, we always accumulate data. But DELETE is IMO an edge case and storage is cheap.

  • We haven’t followed this approach so far, however… It’s true that clients of a GET /resource/{id} might not be looking for some “archived” flag of the resource returned and this change would need to be accounted for. However that change I think is more modest when we think about how clients discover a resource: search. For most of our clients, they’ll be using some search endpoint first to find the resources before the first GET /resource/{id} is ever used. The convention for a search endpoint therefore could be that archived resource items are not returned, unless a query parameter (e.g. ?archived=true) is given.

Leaving it as a 500 error isn’t something we should do. Thoughts?

Best,

Josh

Josh Zamor|josh.zamor@villagereach.org

*Senior Software Development Engineer, OpenLMIS Architect

Village****Reach ** Starting at the Last Mile*

2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA

DIRECT: 1.206.512.1501 CELL: 1.847.504.7262 FAX: 1 206.860.6972

SKYPE: josh.zamor.vr

www.villagereach.org

Connect on Facebook, **Twitter ** and our** Blog**

···


<Soldevelo_logo_EPS_CMYK.png>

Sebastian Brudziński

Software Developer / Team Leader

SolDevelo Sp. z o. o. [LLC]

Office: +48 58 782 45 40
/ Fax: +48 58 782 45 41
Al. Zwycięstwa 96/98
81-451, Gdynia

http://www.soldevelo.com

Place of registration: Regional Court for the City of Gdansk
KRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,
Share capital: 60,000.00 PLN

sbrudzinski@soldevelo.com

Hello,

  although it's true that we won't have foreign key issues in this case, I think we would still don't want archived geographic zones being referenced by active facilities? There might be no hard database integrity error, however we might get a logical integrity error in this case. Do you think this is something we should consider?

Regards,

Paweł

···

On 25.01.2017 18:44, Josh Zamor wrote:

  Hi Sebastian.  I’m of the opinion that in general DELETE shouldn’t delete anything, but rather mark it as deleted/archived.

There are some simple positives:

  • we don’t lose data
    - we don’t have the referential integrity issue you described within a service
    - AND any client to a service that does a GET /resource/{id} will always return the resource once it’s created - that is it wouldn’t return a 200 on day 1, and on day 2 after it’s deleted suddenly return a 404 to the client.  More on this later…

Some cons:

    - since we never delete, we always accumulate data.  But DELETE is IMO an edge case and storage is cheap.
    - We haven’t followed this approach so far, however…  It’s true that clients of a GET /resource/{id} might not be looking for some “archived” flag of the resource returned and this change would need to be accounted for.  However that change I think is more modest when we think about how clients discover a resource: search.  For most of our clients, they’ll be using some search endpoint first to find the resources before the first GET /resource/{id} is ever used.  The convention for a search endpoint therefore could be that archived resource items are not returned, unless a query parameter (e.g. ?archived=true) is given.
      Leaving it as a 500 error isn’t something we should do.  Thoughts?

Best,

Josh

Josh Zamor|

                *                            Senior Software Development Engineer, OpenLMIS Architect 



                     * [**Village****Reach**](http://villagereach.org/)* **Starting at the Last Mile* 

                                            2900 Eastlake Ave. E, Suite 230,  Seattle, WA 98102, USA

                    DIRECT: 1.206.512.1501  CELL: 1.847.504.7262   FAX: 1 206.860.6972

                    SKYPE: josh.zamor.vr

                    [www.villagereach.org](http://www.villagereach.org/)

                    Connect on **[Facebook](https://www.facebook.com/pages/VillageReach/103205113922),** **[Twitter](https://twitter.com/VillageReach) **and our[** Blog** ](http://villagereach.org/see-our-blog/thoughts-from-the-last-mile/)
            On Jan 25, 2017, at 7:58 AM, Sebastian Brudziński <sbrudzinski@soldevelo.com                > wrote:
              Hello everyone,



              Today I've been investigating OLMIS-1705 that talks about DELETE endpoints not working. The problem with the delete is that it may affect the not-null constraint in another entity, in which case the resulting SQL query will fail. This is wrapped into the DataIntegrityViolationException. As an example, attempting to delete a geographic zone will fail if there's any facility that is currently linked to that geographic zone (because Facility requires non-null geographic zone).



              I've been wondering about the approach we want to take to handle those errors. What currently happens is that the delete throws DataIntegrityViolationException, which is translated into 500 error code. We could try to implement manual validations before the actual delete happens, but we've got quite a lot of the not-null constraints scattered across the services and having to check all of the instances of let's say 10 entities upon delete somehow doesn't seem like a good solution. Any ideas for other approaches to tackle this? Or should we just leave it be 500 error code?



              Best regards,

              Sebastian.

            You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.

            To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

            To post to this group, send email to
                              . To view this discussion on the web visit . For more options, visit .

  You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.

  To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

  To post to this group, send email to openlmis-dev@googlegroups.com.

  To view this discussion on the web visit [](https://groups.google.com/d/msgid/openlmis-dev/BDB8ACA4-6F39-4FF7-90F3-F7AE23B39214%40villagereach.org?utm_medium=email&utm_source=footer)      . For more options, visit .


Paweł Gesek

    Software Developer / Team Leader

      / +48 690 020 875

SolDevelo Sp. z o. o. [LLC]

     Office:  +48 58 782 45 40 / Fax:  +48 58 782 45 41  Al. Zwycięstwa 96/98  81-451, Gdynia

     [http://www.soldevelo.com](http://www.SolDevelo.com)

               Place of registration: Regional Court for the City of Gdansk            KRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,            Share capital: 60,000.00 PLN

josh.zamor@villagereach.org

<Soldevelo_logo_EPS_CMYK.png>

Sebastian Brudziński

                                          Software Developer / Team Leader

SolDevelo Sp. z o. o. [LLC]

                                        Office:                         +48 58 782 45 40
                  / Fax:                         +48 58 782 45 41
                                          Al. Zwycięstwa 96/98
                  81-451, Gdynia

                  [http://www.soldevelo.com](http://www.soldevelo.com/)

                                          Place of registration: Regional Court for the City of Gdansk
                                          KRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,
                                          Share capital: 60,000.00 PLN

sbrudzinski@soldevelo.comopenlmis-dev@googlegroups.com

https://groups.google.com/d/msgid/openlmis-dev/5888CB0B.6050305%40soldevelo.com
https://groups.google.com/d/optout
https://groups.google.com/d/msgid/openlmis-dev/BDB8ACA4-6F39-4FF7-90F3-F7AE23B39214%40villagereach.org
https://groups.google.com/d/optout
pgesek@soldevelo.com

Thanks for the answer Josh.

  From what you are saying, the DELETE should be more of an "archive", rather than actual remove from the system. Does that mean we do not want to support complete removal at all? I think that raises some more questions.

  What does it mean that a facility is associated with a geographic zone that is marked as deleted? Does this mean that the facility does no longer has got that association? If so, it would be simpler to just remove all non-null constraints and deletes would work just fine as well. However, I believe we actually want to have those checks in place? Or would we treat them as an association that still exists but with an archived resource? If so, do we still consider that association valid? Is it kind of - it doesn't matter that this resource is archived now, it existsed in the past so it's OK?

  About discovering the resource via "/search", this is probably correct when creating new resources. However all the existing instances will simply look for a resource using UUIDs (especially cross-service relationships). In that case they would just fetch the "archived" resources they are linked to. Is that what we want to happen for all of our cross-service associations?

  From what I understand, this approach means revisiting all our searches and introducing the checks for the deleted/archived flag + an option to search for archived resources. We will probably need to revisit our unique constraints as well (we may have duplications if we keep archived instances). Is that refactor something that we want to do in 3.0 for all of our services?

Best regards,

  Sebastian.

···

sbrudzinski@soldevelo.com
On 25.01.2017 18:44, Josh Zamor wrote:

  Hi Sebastian.  I’m of the opinion that in general DELETE shouldn’t delete anything, but rather mark it as deleted/archived.

There are some simple positives:

  • we don’t lose data
    - we don’t have the referential integrity issue you described within a service
    - AND any client to a service that does a GET /resource/{id} will always return the resource once it’s created - that is it wouldn’t return a 200 on day 1, and on day 2 after it’s deleted suddenly return a 404 to the client.  More on this later…

Some cons:

    - since we never delete, we always accumulate data.  But DELETE is IMO an edge case and storage is cheap.
    - We haven’t followed this approach so far, however…  It’s true that clients of a GET /resource/{id} might not be looking for some “archived” flag of the resource returned and this change would need to be accounted for.  However that change I think is more modest when we think about how clients discover a resource: search.  For most of our clients, they’ll be using some search endpoint first to find the resources before the first GET /resource/{id} is ever used.  The convention for a search endpoint therefore could be that archived resource items are not returned, unless a query parameter (e.g. ?archived=true) is given.
      Leaving it as a 500 error isn’t something we should do.  Thoughts?

Best,

Josh

Josh Zamor|josh.zamor@villagereach.org

                *                            Senior Software Development Engineer, OpenLMIS Architect 



                     * [**Village****Reach**](http://villagereach.org/)* **Starting at the Last Mile* 

                                            2900 Eastlake Ave. E, Suite 230,  Seattle, WA 98102, USA

                    DIRECT: 1.206.512.1501  CELL: 1.847.504.7262   FAX: 1 206.860.6972

                    SKYPE: josh.zamor.vr

                    [www.villagereach.org](http://www.villagereach.org/)

                    Connect on **[Facebook](https://www.facebook.com/pages/VillageReach/103205113922),** **[Twitter](https://twitter.com/VillageReach) **and our[** Blog** ](http://villagereach.org/see-our-blog/thoughts-from-the-last-mile/)
            On Jan 25, 2017, at 7:58 AM, Sebastian Brudziński <sbrudzinski@soldevelo.com                > wrote:
              Hello everyone,



              Today I've been investigating OLMIS-1705 that talks about DELETE endpoints not working. The problem with the delete is that it may affect the not-null constraint in another entity, in which case the resulting SQL query will fail. This is wrapped into the DataIntegrityViolationException. As an example, attempting to delete a geographic zone will fail if there's any facility that is currently linked to that geographic zone (because Facility requires non-null geographic zone).



              I've been wondering about the approach we want to take to handle those errors. What currently happens is that the delete throws DataIntegrityViolationException, which is translated into 500 error code. We could try to implement manual validations before the actual delete happens, but we've got quite a lot of the not-null constraints scattered across the services and having to check all of the instances of let's say 10 entities upon delete somehow doesn't seem like a good solution. Any ideas for other approaches to tackle this? Or should we just leave it be 500 error code?



              Best regards,

              Sebastian.

            You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.

            To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

            To post to this group, send email to
              openlmis-dev@googlegroups.com.

            To view this discussion on the web visit [

https://groups.google.com/d/msgid/openlmis-dev/5888CB0B.6050305%40soldevelo.com](https://groups.google.com/d/msgid/openlmis-dev/5888CB0B.6050305%40soldevelo.com?utm_medium=email&utm_source=footer).

            For more options, visit [https://groups.google.com/d/optout](https://groups.google.com/d/optout).

  You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.

  To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

  To post to this group, send email to openlmis-dev@googlegroups.com.

  To view this discussion on the web visit [https://groups.google.com/d/msgid/openlmis-dev/BDB8ACA4-6F39-4FF7-90F3-F7AE23B39214%40villagereach.org](https://groups.google.com/d/msgid/openlmis-dev/BDB8ACA4-6F39-4FF7-90F3-F7AE23B39214%40villagereach.org?utm_medium=email&utm_source=footer).

  For more options, visit [https://groups.google.com/d/optout](https://groups.google.com/d/optout).


<Soldevelo_logo_EPS_CMYK.png>

Sebastian Brudziński

                                          Software Developer / Team Leader

SolDevelo Sp. z o. o. [LLC]

                                        Office:                         +48 58 782 45 40
                  / Fax:                         +48 58 782 45 41
                                          Al. Zwycięstwa 96/98
                  81-451, Gdynia

                  [http://www.soldevelo.com](http://www.soldevelo.com/)

                                          Place of registration: Regional Court for the City of Gdansk
                                          KRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,
                                          Share capital: 60,000.00 PLN

sbrudzinski@soldevelo.com

Lets step back and be specific about certain endpoints.

DELETE /api/geographicZones/{id}

To answer the question:

“what happens to the facilities that are listed as being in the GeoGraphic zone, when we delete the zone?”

This should have been asked when we implemented the DELETE endpoint. Or when we associated the Facility with the GeographicZone. Right? Was it asked? I could expound on this however we all know the answer, and the answer is that we failed when implementing DELETE /api/geographicZone/{id}. If we didn’t ask the question, why was it even implemented? I can and I will define the current 500 error an indicator that we didn’t probe for and understand the requirements when we implemented the endpoint. I’ll also say I’m glad it does return a 500, as it’s catching a violation of a known requirement: facilities are associated with a geographic zone. Lets not remove that until we know what should be done.

Luckily we’re talking about an edge case of an administrative function, so if 3.0 ships with this we can mark it as our first minor bug and then finally ask the correct “business logic” question - what should be done if we “delete” a geographic zone that has facilities attached.

Lets get to that, however for now lets answer the following:

  • How many endpoints implement the DELETE operation?
  • How many of those endpoints implement DELETE like /api/geographicZones/{id} ? i.e. lead to 500 level responses?
  • Are there other endpoints, which implement DELETE, which likely wouldn’t lead to a 500 level response, which we’re concerned about? What about DELETE /api/requisitions/{id} ?

I did a quick survey of the Reference Data service and feel pretty comfortable saying that all 17 DELETE operations are suspect.

Regardless of the approach for deleting/archiving that we’ll eventually want, I’m pretty certain that 3.0 is going to ship at this point either with DELETE endpoints that hopefully will return HTTP 500 when a known business constraint is violated (e.g. a facility being associated), or we’ll decide to remove these endpoints and address the functional need in a later release.

For reference data, I’m leaning toward removing these endpoints / marking them as “not to be used”. Component leads, please answer these three questions for your service.

Thank you Sebastian for bringing this up. This is important to solve, though of course you can see I have concerns about how we ended up with all these DELETE endpoints - especially in reference data.

Best,

Josh

Josh Zamor|josh.zamor@villagereach.org

*Senior Software Development Engineer, OpenLMIS Architect

Village****Reach ** Starting at the Last Mile*

2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA

DIRECT: 1.206.512.1501 CELL: 1.847.504.7262 FAX: 1 206.860.6972

SKYPE: josh.zamor.vr

www.villagereach.org

Connect on Facebook, **Twitter ** and our** Blog**

···

sbrudzinski@soldevelo.com
On 25.01.2017 18:44, Josh Zamor wrote:

Hi Sebastian. I’m of the opinion that in general DELETE shouldn’t delete anything, but rather mark it as deleted/archived.

There are some simple positives:

  • we don’t lose data
  • we don’t have the referential integrity issue you described within a service
  • AND any client to a service that does a GET /resource/{id} will always return the resource once it’s created - that is it wouldn’t return a 200 on day 1, and on day 2 after it’s deleted suddenly return a 404 to the client. More on this later…

Some cons:

  • since we never delete, we always accumulate data. But DELETE is IMO an edge case and storage is cheap.
  • We haven’t followed this approach so far, however… It’s true that clients of a GET /resource/{id} might not be looking for some “archived” flag of the resource returned and this change would need to be accounted for. However that change I think is more modest when we think about how clients discover a resource: search. For most of our clients, they’ll be using some search endpoint first to find the resources before the first GET /resource/{id} is ever used. The convention for a search endpoint therefore could be that archived resource items are not returned, unless a query parameter (e.g. ?archived=true) is given.

Leaving it as a 500 error isn’t something we should do. Thoughts?

Best,

Josh

Josh Zamor|josh.zamor@villagereach.org

*Senior Software Development Engineer, OpenLMIS Architect

Village****Reach ** Starting at the Last Mile*

2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA

DIRECT: 1.206.512.1501 CELL: 1.847.504.7262 FAX: 1 206.860.6972

SKYPE: josh.zamor.vr

www.villagereach.org

Connect on Facebook, **Twitter ** and our** Blog**

On Jan 25, 2017, at 7:58 AM, Sebastian Brudziński sbrudzinski@soldevelo.com wrote:

Hello everyone,

Today I’ve been investigating OLMIS-1705 that talks about DELETE endpoints not working. The problem with the delete is that it may affect the not-null constraint in another entity, in which case the resulting SQL query will fail. This is wrapped into the DataIntegrityViolationException. As an example, attempting to delete a geographic zone will fail if there’s any facility that is currently linked to that geographic zone (because Facility requires non-null geographic zone).

I’ve been wondering about the approach we want to take to handle those errors. What currently happens is that the delete throws DataIntegrityViolationException, which is translated into 500 error code. We could try to implement manual validations before the actual delete happens, but we’ve got quite a lot of the not-null constraints scattered across the services and having to check all of the instances of let’s say 10 entities upon delete somehow doesn’t seem like a good solution. Any ideas for other approaches to tackle this? Or should we just leave it be 500 error code?

Best regards,

Sebastian.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to
openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/5888CB0B.6050305%40soldevelo.com
.

For more options, visit
https://groups.google.com/d/optout
.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to
openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/BDB8ACA4-6F39-4FF7-90F3-F7AE23B39214%40villagereach.org
.

For more options, visit
https://groups.google.com/d/optout
.

<Soldevelo_logo_EPS_CMYK.png>

Sebastian Brudziński

Software Developer / Team Leader

SolDevelo Sp. z o. o. [LLC]

Office: +48 58 782 45 40
/ Fax: +48 58 782 45 41
Al. Zwycięstwa 96/98
81-451, Gdynia

http://www.soldevelo.com

Place of registration: Regional Court for the City of Gdansk
KRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,
Share capital: 60,000.00 PLN

sbrudzinski@soldevelo.com

It also raises questions about RBAC, no?

Having a true delete (with referential integrity) is indeed useful assuming we have audit logging, but probably only for a very, very few users.

···

sbrudzinski@soldevelo.com
On 25.01.2017 18:44, Josh Zamor wrote:

Hi Sebastian. I’m of the opinion that in general DELETE shouldn’t delete anything, but rather mark it as deleted/archived.

There are some simple positives:

  • we don’t lose data
  • we don’t have the referential integrity issue you described within a service
  • AND any client to a service that does a GET /resource/{id} will always return the resource once it’s created - that is it wouldn’t return a 200 on day 1, and on day 2 after it’s deleted suddenly return a 404 to the client. More on this later…

Some cons:

  • since we never delete, we always accumulate data. But DELETE is IMO an edge case and storage is cheap.
  • We haven’t followed this approach so far, however… It’s true that clients of a GET /resource/{id} might not be looking for some “archived” flag of the resource returned and this change would need to be accounted for. However that change I think is more modest when we think about how clients discover a resource: search. For most of our clients, they’ll be using some search endpoint first to find the resources before the first GET /resource/{id} is ever used. The convention for a search endpoint therefore could be that archived resource items are not returned, unless a query parameter (e.g. ?archived=true) is given.

Leaving it as a 500 error isn’t something we should do. Thoughts?

Best,

Josh

Josh Zamor|josh.zamor@villagereach.org

*Senior Software Development Engineer, OpenLMIS Architect

Village****Reach ** Starting at the Last Mile*

2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA

DIRECT: 1.206.512.1501 CELL: 1.847.504.7262 FAX: 1 206.860.6972

SKYPE: josh.zamor.vr

www.villagereach.org

Connect on Facebook, **Twitter ** and our** Blog**

On Jan 25, 2017, at 7:58 AM, Sebastian Brudziński sbrudzinski@soldevelo.com wrote:

Hello everyone,

Today I’ve been investigating OLMIS-1705 that talks about DELETE endpoints not working. The problem with the delete is that it may affect the not-null constraint in another entity, in which case the resulting SQL query will fail. This is wrapped into the DataIntegrityViolationException. As an example, attempting to delete a geographic zone will fail if there’s any facility that is currently linked to that geographic zone (because Facility requires non-null geographic zone).

I’ve been wondering about the approach we want to take to handle those errors. What currently happens is that the delete throws DataIntegrityViolationException, which is translated into 500 error code. We could try to implement manual validations before the actual delete happens, but we’ve got quite a lot of the not-null constraints scattered across the services and having to check all of the instances of let’s say 10 entities upon delete somehow doesn’t seem like a good solution. Any ideas for other approaches to tackle this? Or should we just leave it be 500 error code?

Best regards,

Sebastian.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to
openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/5888CB0B.6050305%40soldevelo.com
.

For more options, visit
https://groups.google.com/d/optout
.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to
openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/BDB8ACA4-6F39-4FF7-90F3-F7AE23B39214%40villagereach.org
.

For more options, visit
https://groups.google.com/d/optout
.

<Soldevelo_logo_EPS_CMYK.png>

Sebastian Brudziński

Software Developer / Team Leader

SolDevelo Sp. z o. o. [LLC]

Office: +48 58 782 45 40/ Fax: +48 58 782 45 41Al. Zwycięstwa 96/98 81-451, Gdynia

http://www.soldevelo.com

Place of registration: Regional Court for the City of Gdansk KRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,Share capital: 60,000.00 PLN

sbrudzinski@soldevelo.com

I’ve taken a look at the requisition and fulfillment services. The requisition service has currently got 3 DELETE endpoints and the fulfillment service has got 4 of them. It looks like both of them have got only 1 endpoint that may be problematic (that is result in 500 error).

For requisition service this is RequisitionTemplate. Requisition is linked to a RequisitionTemplate and it cannot be deleted as long as there's a requisition that's linked to it. Deleting requisition should be handled properly and this is currently even supported on the UI. The users are allowed to delete a requisition as long as it's in initiated state and deleting it means that a new requisition needs to be initiated for that period. The remaining delete is for JasperTemplates and it doesn't directly affect other entities.

For fulfillment service, deleting an Order may be problematic because ProofOfDelivery is linked with an Order that is required. Other delete endpoints include ProofOfDelivery, JasperTemplates and TransferProperties. I'd say there's no problem with the templates and properties, but I'm not sure how much sense does it make to allow deleting proof of deliveries (even though they shouldn't cause 500).

That being said I'd recommend deleting/marking as "not to be used" delete endpoints for requisitionTemplate and order. Endpoint for proofOfDelivery can be considered as well. Thoughts, component leads?

Also, how would you recommend marking the endpoints as "not to be used"?. Should we remove them from RAML completely? (Endpoints that are not present in RAML cannot be used at all and we don't have to remove them from the codebase completely). A milder approach is just leaving a note in the description in RAML and possibly javadocs that they are not to be used, but that may be hardly noticeable for implementers.

Regards,

Sebastian.

···

sbrudzinski@soldevelo.com
On 25.01.2017 22:05, Jake Watson wrote:

It also raises questions about RBAC, no?

        Having a true delete (with referential integrity) is indeed useful assuming we have audit logging, but probably only for a very, very few users.

From: < > on behalf of Josh Zamor <> Wednesday, January 25, 2017 at 12:37 PM Sebastian Brudziński <> “” <> Re: [openlmis-dev] Error handling for deletes causing constraint violations

Lets step back and be specific about certain endpoints.

DELETE /api/geographicZones/{id}

To answer the question:

            “what happens to the facilities that are listed as being in the GeoGraphic zone, when we delete the zone?”
            This should have been asked when we implemented the DELETE endpoint.  Or when we associated the Facility with the GeographicZone.  Right?  Was it asked?  I could expound on this however we all know the answer, and the answer is that we failed when implementing DELETE /api/geographicZone/{id}.  If we didn’t ask the question, why was it even implemented?  I can and I will define the current 500 error an indicator that we didn’t probe for and understand the requirements when we implemented the endpoint.  I’ll also say I’m glad it does return a 500, as it’s catching a violation of a known requirement:  facilities are associated with a geographic zone.  Lets not remove that until we know what should be done.
            Luckily we’re talking about an edge case of an administrative function, so if 3.0 ships with this we can mark it as our first minor bug and then finally ask the correct “business logic” question  - what should be done if we “delete” a geographic zone that has facilities attached.
            Lets get to that, however for now lets answer the following:
  •                 How many endpoints implement the DELETE operation?
    
  •                 How many of those endpoints implement DELETE like /api/geographicZones/{id} ?  i.e. lead to 500 level responses?
    
  •                 Are there other endpoints, which implement DELETE, which likely wouldn’t lead to a 500 level response, which we’re concerned about?  What about DELETE /api/requisitions/{id} ?
    
              I did a quick survey of the Reference Data service and feel pretty comfortable saying that all 17 DELETE operations are suspect.
            Regardless of the approach for deleting/archiving that we’ll eventually want, I’m pretty certain that 3.0 is going to ship at this point either with DELETE endpoints that hopefully will return HTTP 500 when a known business constraint is violated (e.g. a facility being associated), or we’ll decide to remove these endpoints and address the functional need in a later release.
            For reference data, I’m leaning toward removing these endpoints / marking them as “not to be used”.  Component leads, please answer these three questions for your service.
            Thank you Sebastian for bringing this up.  This is important to solve, though of course you can see I have concerns about how we ended up with all these DELETE endpoints - especially in reference data.

Best,

Josh

Josh Zamor|

                          *                                      Senior Software Development Engineer, OpenLMIS Architect 



                               * [**Village****Reach**](http://villagereach.org/)* **Starting at the Last Mile* 

                                                                2900 Eastlake Ave. E, Suite 230,  Seattle, WA 98102, USA

                              DIRECT: 1.206.512.1501  CELL: 1.847.504.7262   FAX: 1 206.860.6972

                              SKYPE: josh.zamor.vr

                              [www.villagereach.org](http://www.villagereach.org/)

                              Connect on **[Facebook](https://www.facebook.com/pages/VillageReach/103205113922),** **[Twitter](https://twitter.com/VillageReach) **and our[** Blog** ](http://villagereach.org/see-our-blog/thoughts-from-the-last-mile/)
                      On Jan 25, 2017, at 11:11 AM, Sebastian Brudziński <                          > wrote:

Thanks for the answer Josh.

                          From what you are saying, the DELETE should be more of an "archive", rather than actual remove from the system. Does that mean we do not want to support complete removal at all? I think that raises some more questions.
                          What does it mean that a facility is associated with a geographic zone that is marked as deleted? Does this mean that the facility does no longer has got that association? If so, it would be simpler to just remove all non-null constraints and deletes would work just fine as well. However, I believe we actually want to have those checks in place? Or would we treat them as an association that still exists but with an archived resource? If so, do we still consider that association valid? Is it kind of - it doesn't matter that this resource is archived now, it existsed in the past so it's OK?
                          About discovering the resource via "/search", this is probably correct when creating new resources. However all the existing instances will simply look for a resource using UUIDs (especially cross-service relationships). In that case they would just fetch the "archived" resources they are linked to. Is that what we want to happen for all of our cross-service associations?
                          From what I understand, this approach means revisiting all our searches and introducing the checks for the deleted/archived flag + an option to search for archived resources. We will probably need to revisit our unique constraints as well (we may have duplications if we keep archived instances). Is that refactor something that we want to do in 3.0 for all of our services?

Best regards,

                          Sebastian.

<Soldevelo_logo_EPS_CMYK.png>

Sebastian Brudziński

                                                          Software Developer / Team Leader
                            SolDevelo Sp. z o. o. [LLC]

                          Office:                                 +48 58 782 45 40                              / Fax: +48 58 782 45 41                                Al. Zwycięstwa 96/98
                            81-451, Gdynia

                          [http://www.soldevelo.com](http://www.soldevelo.com/)



                                                          Place of registration: Regional Court for the City of Gdansk                                KRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,                                Share capital: 60,000.00 PLN

                      You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.

                      To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

                      To post to this group, send email to
                                                  . To view this discussion on the web visit . For more options, visit .

          You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.

          To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

          To post to this group, send email to
                          . To view this discussion on the web visit . For more options, visit .

openlmis-dev@googlegroups.comjosh.zamor@villagereach.org
Date:
To: sbrudzinski@soldevelo.com
Cc: openlmis-dev@googlegroups.comopenlmis-dev@googlegroups.com
Subject:
josh.zamor@villagereach.orgsbrudzinski@soldevelo.comsbrudzinski@soldevelo.com
On 25.01.2017 18:44, Josh Zamor wrote:

                          Hi Sebastian.  I’m of the opinion that in general DELETE shouldn’t delete anything, but rather mark it as deleted/archived.
                            There are some simple positives:
  • we don’t lose data
                            - we don’t have the referential integrity issue you described within a service
                            - AND any client to a service that does a GET /resource/{id} will always return the resource once it’s created - that is it wouldn’t return a 200 on day 1, and on day 2 after it’s deleted suddenly return a 404 to the client.  More on this later…

Some cons:

                            - since we never delete, we always accumulate data.  But DELETE is IMO an edge case and storage is cheap.
                            - We haven’t followed this approach so far, however…  It’s true that clients of a GET /resource/{id} might not be looking for some “archived” flag of the resource returned and this change would need to be accounted for.  However that change I think is more modest when we think about how clients discover a resource: search.  For most of our clients, they’ll be using some search endpoint first to find the resources before the first GET /resource/{id} is ever used.  The convention for a search endpoint therefore could be that archived resource items are not returned, unless a query parameter (e.g. ?archived=true) is given.
                              Leaving it as a 500 error isn’t something we should do.  Thoughts?

Best,

Josh

                                                Josh Zamor| 

                                        *                                                    Senior Software Development Engineer, OpenLMIS Architect 



                                             * [**Village****Reach**](http://villagereach.org/)* **                                                    Starting at the Last Mile* 

                                                                                            2900 Eastlake Ave. E, Suite 230,  Seattle, WA 98102, USA

                                            DIRECT: 1.206.512.1501  CELL: 1.847.504.7262   FAX: 1 206.860.6972

                                            SKYPE: josh.zamor.vr

                                            [](http://www.villagereach.org/)
                                            Connect on**[Facebook](https://www.facebook.com/pages/VillageReach/103205113922),** **[Twitter](https://twitter.com/VillageReach) **and our[** Blog** ](http://villagereach.org/see-our-blog/thoughts-from-the-last-mile/)
                                    On Jan 25, 2017, at 7:58 AM, Sebastian Brudziński <sbrudzinski@soldevelo.com                                        > wrote:
                                      Hello everyone,



                                      Today I've been investigating OLMIS-1705 that talks about DELETE endpoints not working. The problem with the delete is that it may affect the not-null constraint in another entity, in which case the resulting SQL query will fail. This is wrapped into the DataIntegrityViolationException. As an example, attempting to delete a geographic zone will fail if there's any facility that is currently linked to that geographic zone (because Facility requires non-null geographic zone).



                                      I've been wondering about the approach we want to take to handle those errors. What currently happens is that the delete throws DataIntegrityViolationException, which is translated into 500 error code. We could try to implement manual validations before the actual delete happens, but we've got quite a lot of the not-null constraints scattered across the services and having to check all of the instances of let's say 10 entities upon delete somehow doesn't seem like a good solution. Any ideas for other approaches to tackle this? Or should we just leave it be 500 error code?



                                      Best regards,

                                      Sebastian.

                                    You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.

                                    To unsubscribe from this group and stop receiving emails from it, send an email to

openlmis-dev+unsubscribe@googlegroups.com.

                                    To post to this group, send email to
                                                                              . To view this discussion on the web visit . For more options, visit .

                          You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.

                          To unsubscribe from this group and stop receiving emails from it, send an email to

openlmis-dev+unsubscribe@googlegroups.com.

                          To post to this group, send email to
                                                          . To view this discussion on the web visit . For more options, visit .

josh.zamor@villagereach.orgwww.villagereach.org

                                        --

<Soldevelo_logo_EPS_CMYK.png>

                                            Sebastian Brudziński

                                                                                          Software Developer / Team Leader

                                            SolDevelo Sp. z o. o. [LLC]

                                          Office:                                                 +48 58 782 45 40                                              / Fax:                                                 +48 58 782 45 41                                                Al. Zwycięstwa 96/9881-451, Gdynia

                                          [](http://www.soldevelo.com/)

sbrudzinski@soldevelo.comhttp://www.soldevelo.com

                                            Place of registration: Regional Court for the City of Gdansk                                                KRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,                                                Share capital: 60,000.00 PLNopenlmis-dev@googlegroups.com


https://groups.google.com/d/msgid/openlmis-dev/5888CB0B.6050305%40soldevelo.com

https://groups.google.com/d/optout
openlmis-dev@googlegroups.com

https://groups.google.com/d/msgid/openlmis-dev/BDB8ACA4-6F39-4FF7-90F3-F7AE23B39214%40villagereach.org

https://groups.google.com/d/optout

openlmis-dev@googlegroups.com

https://groups.google.com/d/msgid/openlmis-dev/a620c1fb-56b0-fb4e-e69d-318a048aab05%40soldevelo.com
https://groups.google.com/d/optout
openlmis-dev@googlegroups.com
https://groups.google.com/d/msgid/openlmis-dev/765B70FF-DC5A-4829-A518-6F3DBD4EA230%40villagereach.org
https://groups.google.com/d/optout

Hi Sebastian and Josh,

For the RequisitionTemplate DELETE endpoint, I suggest we simply prohibit deletion of any RequisitionTemplate that has 1 or more Requisitions. That should involve adding a simple check before deletion and responding with a 400 status code* and an error message.

For RequisitionTemplate I don’t think we need to do the complex thing of marking a deleted item as “Archived”. If there are zero Requisitions, then we truly allow the user to delete the RequisitionTemplate altogether, and that is sufficient. This is not one of our high-value endpoints where we want to archive old records or store a long audit trail. I don’t think an “Archived” status for RequisitionTemplate data is worth the complexity.

*HTTP 422 could also appropriate. OpenLMIS service style guide points to this:
http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#http-status
. I don’t think we have any consistency yet about what to return when DELETEs are not allowed. So I’m fine with 400 or 422.

Sebastian , this ticket 1705 seems to touch a lot of components and it has a lot of different DELETE endpoints to address. If we should split the ticket to address different services/components on their own, that’s fine. Also, if it would help to have a Skype meeting to speed up our decision-making, feel free to schedule.

-Brandon

···

From: openlmis-dev@googlegroups.com on behalf of Sebastian Brudziński sbrudzinski@soldevelo.com

Date: Friday, January 27, 2017 at 6:41 AM

To: "openlmis-dev@googlegroups.com" openlmis-dev@googlegroups.com

Subject: Re: [openlmis-dev] Error handling for deletes causing constraint violations

I’ve taken a look at the requisition and fulfillment services. The requisition service has currently got 3 DELETE endpoints and the fulfillment service has got 4 of them. It looks like both of them have got only 1 endpoint that may be problematic (that is result in 500 error).

For requisition service this is RequisitionTemplate. Requisition is linked to a RequisitionTemplate and it cannot be deleted as long as there’s a requisition that’s linked to it. Deleting requisition should be handled properly and this is currently even supported on the UI. The users are allowed to delete a requisition as long as it’s in initiated state and deleting it means that a new requisition needs to be initiated for that period. The remaining delete is for JasperTemplates and it doesn’t directly affect other entities.

For fulfillment service, deleting an Order may be problematic because ProofOfDelivery is linked with an Order that is required. Other delete endpoints include ProofOfDelivery, JasperTemplates and TransferProperties. I’d say there’s no problem with the templates and properties, but I’m not sure how much sense does it make to allow deleting proof of deliveries (even though they shouldn’t cause 500).

That being said I’d recommend deleting/marking as “not to be used” delete endpoints for requisitionTemplate and order. Endpoint for proofOfDelivery can be considered as well. Thoughts, component leads?

Also, how would you recommend marking the endpoints as “not to be used”?. Should we remove them from RAML completely? (Endpoints that are not present in RAML cannot be used at all and we don’t have to remove them from the codebase completely). A milder approach is just leaving a note in the description in RAML and possibly javadocs that they are not to be used, but that may be hardly noticeable for implementers.

Regards,

Sebastian.

Sebastian Brudziński

Software Developer / Team Leader

sbrudzinski@soldevelo.com

SolDevelo Sp. z o. o. [LLC]

Office: +48 58 782 45 40 / Fax: +48 58 782 45 41 Al. Zwycięstwa 96/98 81-451, Gdynia

http://www.soldevelo.com

Place of registration: Regional Court for the City of Gdansk KRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585, Share capital: 60,000.00 PLN

On 25.01.2017 22:05, Jake Watson wrote:

It also raises questions about RBAC, no?

Having a true delete (with referential integrity) is indeed useful assuming we have audit logging, but probably only for a very, very few users.

From: openlmis-dev@googlegroups.com on behalf of Josh Zamor josh.zamor@villagereach.org

Date: Wednesday, January 25, 2017 at 12:37 PM

To: Sebastian Brudziński sbrudzinski@soldevelo.com

Cc: "openlmis-dev@googlegroups.com" openlmis-dev@googlegroups.com

Subject: Re: [openlmis-dev] Error handling for deletes causing constraint violations

Lets step back and be specific about certain endpoints.

DELETE /api/geographicZones/{id}

To answer the question:

“what happens to the facilities that are listed as being in the GeoGraphic zone, when we delete the zone?”

This should have been asked when we implemented the DELETE endpoint. Or when we associated the Facility with the GeographicZone. Right? Was it asked? I could expound on this however we all know the answer, and the answer is that we failed when implementing DELETE /api/geographicZone/{id}. If we didn’t ask the question, why was it even implemented? I can and I will define the current 500 error an indicator that we didn’t probe for and understand the requirements when we implemented the endpoint. I’ll also say I’m glad it does return a 500, as it’s catching a violation of a known requirement: facilities are associated with a geographic zone. Lets not remove that until we know what should be done.

Luckily we’re talking about an edge case of an administrative function, so if 3.0 ships with this we can mark it as our first minor bug and then finally ask the correct “business logic” question - what should be done if we “delete” a geographic zone that has facilities attached.

Lets get to that, however for now lets answer the following:

  • How many endpoints implement the DELETE operation?
  • How many of those endpoints implement DELETE like /api/geographicZones/{id} ? i.e. lead to 500 level responses?
  • Are there other endpoints, which implement DELETE, which likely wouldn’t lead to a 500 level response, which we’re concerned about? What about DELETE /api/requisitions/{id} ?

I did a quick survey of the Reference Data service and feel pretty comfortable saying that all 17 DELETE operations are suspect.

Regardless of the approach for deleting/archiving that we’ll eventually want, I’m pretty certain that 3.0 is going to ship at this point either with DELETE endpoints that hopefully will return HTTP 500 when a known business constraint is violated (e.g. a facility being associated), or we’ll decide to remove these endpoints and address the functional need in a later release.

For reference data, I’m leaning toward removing these endpoints / marking them as “not to be used”. Component leads, please answer these three questions for your service.

Thank you Sebastian for bringing this up. This is important to solve, though of course you can see I have concerns about how we ended up with all these DELETE endpoints - especially in reference data.

Best,

Josh

Josh Zamor|josh.zamor@villagereach.org

*Senior Software Development Engineer, OpenLMIS Architect

Village****Reach ** Starting at the Last Mile*

2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA

DIRECT: 1.206.512.1501 CELL: 1.847.504.7262 FAX: 1 206.860.6972

SKYPE: josh.zamor.vr

www.villagereach.org

Connect on Facebook, **Twitter ** and our** **Blog

On Jan 25, 2017, at 11:11 AM, Sebastian Brudziński sbrudzinski@soldevelo.com wrote:

Thanks for the answer Josh.

From what you are saying, the DELETE should be more of an “archive”, rather than actual remove from the system. Does that mean we do not want to support complete removal at all? I think that raises some more questions.

What does it mean that a facility is associated with a geographic zone that is marked as deleted? Does this mean that the facility does no longer has got that association? If so, it would be simpler to just remove all non-null constraints and deletes would work just fine as well. However, I believe we actually want to have those checks in place? Or would we treat them as an association that still exists but with an archived resource? If so, do we still consider that association valid? Is it kind of - it doesn’t matter that this resource is archived now, it existsed in the past so it’s OK?

About discovering the resource via “/search”, this is probably correct when creating new resources. However all the existing instances will simply look for a resource using UUIDs (especially cross-service relationships). In that case they would just fetch the “archived” resources they are linked to. Is that what we want to happen for all of our cross-service associations?

From what I understand, this approach means revisiting all our searches and introducing the checks for the deleted/archived flag + an option to search for archived resources. We will probably need to revisit our unique constraints as well (we may have duplications if we keep archived instances). Is that refactor something that we want to do in 3.0 for all of our services?

Best regards,

Sebastian.

<Soldevelo_logo_EPS_CMYK.png>

Sebastian Brudziński

Software Developer / Team Leader

sbrudzinski@soldevelo.com

SolDevelo Sp. z o. o. [LLC]

Office: +48 58 782 45 40/ Fax: +48 58 782 45 41Al. Zwycięstwa 96/9881-451, Gdynia

http://www.soldevelo.com

Place of registration: Regional Court for the City of GdanskKRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,Share capital: 60,000.00 PLN

On 25.01.2017 18:44, Josh Zamor wrote:

Hi Sebastian. I’m of the opinion that in general DELETE shouldn’t delete anything, but rather mark it as deleted/archived.

There are some simple positives:

  • we don’t lose data
  • we don’t have the referential integrity issue you described within a service
  • AND any client to a service that does a GET /resource/{id} will always return the resource once it’s created - that is it wouldn’t return a 200 on day 1, and on day 2 after it’s deleted suddenly return a 404 to the client. More on this later…

Some cons:

  • since we never delete, we always accumulate data. But DELETE is IMO an edge case and storage is cheap.
  • We haven’t followed this approach so far, however… It’s true that clients of a GET /resource/{id} might not be looking for some “archived” flag of the resource returned and this change would need to be accounted for. However that change I think is more modest when we think about how clients discover a resource: search. For most of our clients, they’ll be using some search endpoint first to find the resources before the first GET /resource/{id} is ever used. The convention for a search endpoint therefore could be that archived resource items are not returned, unless a query parameter (e.g. ?archived=true) is given.

Leaving it as a 500 error isn’t something we should do. Thoughts?

Best,

Josh

Josh Zamor|josh.zamor@villagereach.org

*Senior Software Development Engineer, OpenLMIS Architect

Village****Reach ** Starting at the Last Mile*

2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA

DIRECT: 1.206.512.1501 CELL: 1.847.504.7262 FAX: 1 206.860.6972

SKYPE: josh.zamor.vr

www.villagereach.org

Connect on Facebook, **Twitter ** and our** **Blog

On Jan 25, 2017, at 7:58 AM, Sebastian Brudziński sbrudzinski@soldevelo.com wrote:

Hello everyone,

Today I’ve been investigating OLMIS-1705 that talks about DELETE endpoints not working. The problem with the delete is that it may affect the not-null constraint in another entity, in which case the resulting SQL query will fail. This is wrapped into the DataIntegrityViolationException. As an example, attempting to delete a geographic zone will fail if there’s any facility that is currently linked to that geographic zone (because Facility requires non-null geographic zone).

I’ve been wondering about the approach we want to take to handle those errors. What currently happens is that the delete throws DataIntegrityViolationException, which is translated into 500 error code. We could try to implement manual validations before the actual delete happens, but we’ve got quite a lot of the not-null constraints scattered across the services and having to check all of the instances of let’s say 10 entities upon delete somehow doesn’t seem like a good solution. Any ideas for other approaches to tackle this? Or should we just leave it be 500 error code?

Best regards,

Sebastian.

<Soldevelo_logo_EPS_CMYK.png>

Sebastian Brudziński

Software Developer / Team Leader

sbrudzinski@soldevelo.com

SolDevelo Sp. z o. o. [LLC]

Office: +48 58 782 45 40/ Fax: +48 58 782 45 41Al. Zwycięstwa 96/9881-451, Gdynia

http://www.soldevelo.com

Place of registration: Regional Court for the City of GdanskKRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,Share capital: 60,000.00 PLN

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/5888CB0B.6050305%40soldevelo.com
.

For more options, visit https://groups.google.com/d/optout.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/BDB8ACA4-6F39-4FF7-90F3-F7AE23B39214%40villagereach.org
.

For more options, visit https://groups.google.com/d/optout.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/a620c1fb-56b0-fb4e-e69d-318a048aab05%40soldevelo.com
.

For more options, visit https://groups.google.com/d/optout.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/765B70FF-DC5A-4829-A518-6F3DBD4EA230%40villagereach.org
.

For more options, visit https://groups.google.com/d/optout.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/588B5C04.10403%40soldevelo.com
.

For more options, visit https://groups.google.com/d/optout.

Hi Sebastian,

Regarding the RequisitionTemplate, I want to add one more thing: Adding this simple DELETE ability on a RequisitionTemplate should not change what you folks did in ticket OLMIS-1360*. Specifically, that ticket means that each time a requisition is created, it uses the current template at that time. Even if the template changes later, that does not break the existing requisitions.

I mention that here because hopefully even if a Requisition Template that has existing Requisitions gets deleted, that probably should not break the Requisition any way.

In summary, for the RequisitionTemplate DELETE endpoint, I will be happy if we do the simple solution for deleting that I suggested in my previous message below, AND as long as we make sure 1360 still works.

-Brandon

···

On Sunday, January 29, 2017 at 3:00:42 PM UTC-8, brandon.bowersox-johnson wrote:

Hi Sebastian and Josh,

For the RequisitionTemplate DELETE endpoint, I suggest we simply prohibit deletion of any RequisitionTemplate that has 1 or more Requisitions. That should involve adding a simple check before deletion and responding with a 400 status code* and an error message.

For RequisitionTemplate I don’t think we need to do the complex thing of marking a deleted item as “Archived”. If there are zero Requisitions, then we truly allow the user to delete the RequisitionTemplate altogether, and that is sufficient. This is not one of our high-value endpoints where we want to archive old records or store a long audit trail. I don’t think an “Archived” status for RequisitionTemplate data is worth the complexity.

*HTTP 422 could also appropriate. OpenLMIS service style guide points to this:
http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#http-status
. I don’t think we have any consistency yet about what to return when DELETEs are not allowed. So I’m fine with 400 or 422.

Sebastian , this ticket 1705 seems to touch a lot of components and it has a lot of different DELETE endpoints to address. If we should split the ticket to address different services/components on their own, that’s fine. Also, if it would help to have a Skype meeting to speed up our decision-making, feel free to schedule.

-Brandon

From: openlmis-dev@googlegroups.com on behalf of Sebastian Brudziński sbrudzinski@soldevelo.com

Date: Friday, January 27, 2017 at 6:41 AM

To:openlmis-dev@googlegroups.comopenlmis-dev@googlegroups.com

Subject: Re: [openlmis-dev] Error handling for deletes causing constraint violations

I’ve taken a look at the requisition and fulfillment services. The requisition service has currently got 3 DELETE endpoints and the fulfillment service has got 4 of them. It looks like both of them have got only 1 endpoint that may be problematic (that is result in 500 error).

For requisition service this is RequisitionTemplate. Requisition is linked to a RequisitionTemplate and it cannot be deleted as long as there’s a requisition that’s linked to it. Deleting requisition should be handled properly and this is currently even supported on the UI. The users are allowed to delete a requisition as long as it’s in initiated state and deleting it means that a new requisition needs to be initiated for that period. The remaining delete is for JasperTemplates and it doesn’t directly affect other entities.

For fulfillment service, deleting an Order may be problematic because ProofOfDelivery is linked with an Order that is required. Other delete endpoints include ProofOfDelivery, JasperTemplates and TransferProperties. I’d say there’s no problem with the templates and properties, but I’m not sure how much sense does it make to allow deleting proof of deliveries (even though they shouldn’t cause 500).

That being said I’d recommend deleting/marking as “not to be used” delete endpoints for requisitionTemplate and order. Endpoint for proofOfDelivery can be considered as well. Thoughts, component leads?

Also, how would you recommend marking the endpoints as “not to be used”?. Should we remove them from RAML completely? (Endpoints that are not present in RAML cannot be used at all and we don’t have to remove them from the codebase completely). A milder approach is just leaving a note in the description in RAML and possibly javadocs that they are not to be used, but that may be hardly noticeable for implementers.

Regards,

Sebastian.

Sebastian Brudziński

Software Developer / Team Leader

sbrudzinski@soldevelo.com

SolDevelo Sp. z o. o. [LLC]

Office: +48 58 782 45 40 / Fax: +48 58 782 45 41 Al. Zwycięstwa 96/98 81-451, Gdynia

http://www.soldevelo.com

Place of registration: Regional Court for the City of Gdansk KRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585, Share capital: 60,000.00 PLN

On 25.01.2017 22:05, Jake Watson wrote:

It also raises questions about RBAC, no?

Having a true delete (with referential integrity) is indeed useful assuming we have audit logging, but probably only for a very, very few users.

From: openlmis-dev@googlegroups.com on behalf of Josh Zamor josh.zamor@villagereach.org

Date: Wednesday, January 25, 2017 at 12:37 PM

To: Sebastian Brudziński sbrudzinski@soldevelo.com

Cc:openlmis-dev@googlegroups.comopenlmis-dev@googlegroups.com

Subject: Re: [openlmis-dev] Error handling for deletes causing constraint violations

Lets step back and be specific about certain endpoints.

DELETE /api/geographicZones/{id}

To answer the question:

“what happens to the facilities that are listed as being in the GeoGraphic zone, when we delete the zone?”

This should have been asked when we implemented the DELETE endpoint. Or when we associated the Facility with the GeographicZone. Right? Was it asked? I could expound on this however we all know the answer, and the answer is that we failed when implementing DELETE /api/geographicZone/{id}. If we didn’t ask the question, why was it even implemented? I can and I will define the current 500 error an indicator that we didn’t probe for and understand the requirements when we implemented the endpoint. I’ll also say I’m glad it does return a 500, as it’s catching a violation of a known requirement: facilities are associated with a geographic zone. Lets not remove that until we know what should be done.

Luckily we’re talking about an edge case of an administrative function, so if 3.0 ships with this we can mark it as our first minor bug and then finally ask the correct “business logic” question - what should be done if we “delete” a geographic zone that has facilities attached.

Lets get to that, however for now lets answer the following:

  • How many endpoints implement the DELETE operation?
  • How many of those endpoints implement DELETE like /api/geographicZones/{id} ? i.e. lead to 500 level responses?
  • Are there other endpoints, which implement DELETE, which likely wouldn’t lead to a 500 level response, which we’re concerned about? What about DELETE /api/requisitions/{id} ?

I did a quick survey of the Reference Data service and feel pretty comfortable saying that all 17 DELETE operations are suspect.

Regardless of the approach for deleting/archiving that we’ll eventually want, I’m pretty certain that 3.0 is going to ship at this point either with DELETE endpoints that hopefully will return HTTP 500 when a known business constraint is violated (e.g. a facility being associated), or we’ll decide to remove these endpoints and address the functional need in a later release.

For reference data, I’m leaning toward removing these endpoints / marking them as “not to be used”. Component leads, please answer these three questions for your service.

Thank you Sebastian for bringing this up. This is important to solve, though of course you can see I have concerns about how we ended up with all these DELETE endpoints - especially in reference data.

Best,

Josh

Josh Zamor|josh.zamor@villagereach.org

*Senior Software Development Engineer, OpenLMIS Architect

Village****Reach ** Starting at the Last Mile*

2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA

DIRECT: 1.206.512.1501 CELL: 1.847.504.7262 FAX: 1 206.860.6972

SKYPE: josh.zamor.vr

www.villagereach.org

Connect on Facebook, **Twitter ** and our** **Blog

On Jan 25, 2017, at 11:11 AM, Sebastian Brudziński sbrudzinski@soldevelo.com wrote:

Thanks for the answer Josh.

From what you are saying, the DELETE should be more of an “archive”, rather than actual remove from the system. Does that mean we do not want to support complete removal at all? I think that raises some more questions.

What does it mean that a facility is associated with a geographic zone that is marked as deleted? Does this mean that the facility does no longer has got that association? If so, it would be simpler to just remove all non-null constraints and deletes would work just fine as well. However, I believe we actually want to have those checks in place? Or would we treat them as an association that still exists but with an archived resource? If so, do we still consider that association valid? Is it kind of - it doesn’t matter that this resource is archived now, it existsed in the past so it’s OK?

About discovering the resource via “/search”, this is probably correct when creating new resources. However all the existing instances will simply look for a resource using UUIDs (especially cross-service relationships). In that case they would just fetch the “archived” resources they are linked to. Is that what we want to happen for all of our cross-service associations?

From what I understand, this approach means revisiting all our searches and introducing the checks for the deleted/archived flag + an option to search for archived resources. We will probably need to revisit our unique constraints as well (we may have duplications if we keep archived instances). Is that refactor something that we want to do in 3.0 for all of our services?

Best regards,

Sebastian.

<Soldevelo_logo_EPS_CMYK.png>

Sebastian Brudziński

Software Developer / Team Leader

sbrudzinski@soldevelo.com

SolDevelo Sp. z o. o. [LLC]

Office: +48 58 782 45 40/ Fax: +48 58 782 45 41Al. Zwycięstwa 96/9881-451, Gdynia

http://www.soldevelo.com

Place of registration: Regional Court for the City of GdanskKRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,Share capital: 60,000.00 PLN

On 25.01.2017 18:44, Josh Zamor wrote:

Hi Sebastian. I’m of the opinion that in general DELETE shouldn’t delete anything, but rather mark it as deleted/archived.

There are some simple positives:

  • we don’t lose data
  • we don’t have the referential integrity issue you described within a service
  • AND any client to a service that does a GET /resource/{id} will always return the resource once it’s created - that is it wouldn’t return a 200 on day 1, and on day 2 after it’s deleted suddenly return a 404 to the client. More on this later…

Some cons:

  • since we never delete, we always accumulate data. But DELETE is IMO an edge case and storage is cheap.
  • We haven’t followed this approach so far, however… It’s true that clients of a GET /resource/{id} might not be looking for some “archived” flag of the resource returned and this change would need to be accounted for. However that change I think is more modest when we think about how clients discover a resource: search. For most of our clients, they’ll be using some search endpoint first to find the resources before the first GET /resource/{id} is ever used. The convention for a search endpoint therefore could be that archived resource items are not returned, unless a query parameter (e.g. ?archived=true) is given.

Leaving it as a 500 error isn’t something we should do. Thoughts?

Best,

Josh

Josh Zamor|josh.zamor@villagereach.org

*Senior Software Development Engineer, OpenLMIS Architect

Village****Reach ** Starting at the Last Mile*

2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA

DIRECT: 1.206.512.1501 CELL: 1.847.504.7262 FAX: 1 206.860.6972

SKYPE: josh.zamor.vr

www.villagereach.org

Connect on Facebook, **Twitter ** and our** **Blog

On Jan 25, 2017, at 7:58 AM, Sebastian Brudziński sbrudzinski@soldevelo.com wrote:

Hello everyone,

Today I’ve been investigating OLMIS-1705 that talks about DELETE endpoints not working. The problem with the delete is that it may affect the not-null constraint in another entity, in which case the resulting SQL query will fail. This is wrapped into the DataIntegrityViolationExceptio n. As an example, attempting to delete a geographic zone will fail if there’s any facility that is currently linked to that geographic zone (because Facility requires non-null geographic zone).

I’ve been wondering about the approach we want to take to handle those errors. What currently happens is that the delete throws DataIntegrityViolationExceptio n, which is translated into 500 error code. We could try to implement manual validations before the actual delete happens, but we’ve got quite a lot of the not-null constraints scattered across the services and having to check all of the instances of let’s say 10 entities upon delete somehow doesn’t seem like a good solution. Any ideas for other approaches to tackle this? Or should we just leave it be 500 error code?

Best regards,

Sebastian.

<Soldevelo_logo_EPS_CMYK.png>

Sebastian Brudziński

Software Developer / Team Leader

sbrudzinski@soldevelo.com

SolDevelo Sp. z o. o. [LLC]

Office: +48 58 782 45 40/ Fax: +48 58 782 45 41Al. Zwycięstwa 96/9881-451, Gdynia

http://www.soldevelo.com

Place of registration: Regional Court for the City of GdanskKRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,Share capital: 60,000.00 PLN

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/5888CB0B.6050305%40soldevelo.com
.

For more options, visit https://groups.google.com/d/optout.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/BDB8ACA4-6F39-4FF7-90F3-F7AE23B39214%40villagereach.org
.

For more options, visit https://groups.google.com/d/optout.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/a620c1fb-56b0-fb4e-e69d-318a048aab05%40soldevelo.com
.

For more options, visit https://groups.google.com/d/optout.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/765B70FF-DC5A-4829-A518-6F3DBD4EA230%40villagereach.org
.

For more options, visit https://groups.google.com/d/optout.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/588B5C04.10403%40soldevelo.com
.

For more options, visit https://groups.google.com/d/optout.

I share Brandon’s thoughts on the Requisition Template.

Thanks Sebastian for tracking those down. I think from that list, we’ll proceed by treating those endpoints separately from the DELETE endpoints of Reference Data.

For Reference Data, which appears to have by far the most DELETE endpoints, I’d be fine with simply adding to the RAML not to use the DELETE operation for each. However I could also see a value to removing access to the endpoint altogether to safe guard against accidental usage. Sebastian, since SolDevelo will be the first organization to implement 3.0, I’d like to hear your opinion: Would you prefer to remove them to be safe, or can we do the least-effort and simply recommend against their usage in the API documentation?

Best,
Josh

···

On Monday, January 30, 2017 at 5:01:37 PM UTC-8, brandon.bowersox-johnson wrote:

Hi Sebastian,

Regarding the RequisitionTemplate, I want to add one more thing: Adding this simple DELETE ability on a RequisitionTemplate should not change what you folks did in ticket OLMIS-1360*. Specifically, that ticket means that each time a requisition is created, it uses the current template at that time. Even if the template changes later, that does not break the existing requisitions.

I mention that here because hopefully even if a Requisition Template that has existing Requisitions gets deleted, that probably should not break the Requisition any way.

In summary, for the RequisitionTemplate DELETE endpoint, I will be happy if we do the simple solution for deleting that I suggested in my previous message below, AND as long as we make sure 1360 still works.

-Brandon

On Sunday, January 29, 2017 at 3:00:42 PM UTC-8, brandon.bowersox-johnson wrote:

Hi Sebastian and Josh,

For the RequisitionTemplate DELETE endpoint, I suggest we simply prohibit deletion of any RequisitionTemplate that has 1 or more Requisitions. That should involve adding a simple check before deletion and responding with a 400 status code* and an error message.

For RequisitionTemplate I don’t think we need to do the complex thing of marking a deleted item as “Archived”. If there are zero Requisitions, then we truly allow the user to delete the RequisitionTemplate altogether, and that is sufficient. This is not one of our high-value endpoints where we want to archive old records or store a long audit trail. I don’t think an “Archived” status for RequisitionTemplate data is worth the complexity.

*HTTP 422 could also appropriate. OpenLMIS service style guide points to this:
http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#http-status
. I don’t think we have any consistency yet about what to return when DELETEs are not allowed. So I’m fine with 400 or 422.

Sebastian , this ticket 1705 seems to touch a lot of components and it has a lot of different DELETE endpoints to address. If we should split the ticket to address different services/components on their own, that’s fine. Also, if it would help to have a Skype meeting to speed up our decision-making, feel free to schedule.

-Brandon

From: openlmis-dev@googlegroups.com on behalf of Sebastian Brudziński sbrudzinski@soldevelo.com

Date: Friday, January 27, 2017 at 6:41 AM

To:openlmis-dev@googlegroups.comopenlmis-dev@googlegroups.com

Subject: Re: [openlmis-dev] Error handling for deletes causing constraint violations

I’ve taken a look at the requisition and fulfillment services. The requisition service has currently got 3 DELETE endpoints and the fulfillment service has got 4 of them. It looks like both of them have got only 1 endpoint that may be problematic (that is result in 500 error).

For requisition service this is RequisitionTemplate. Requisition is linked to a RequisitionTemplate and it cannot be deleted as long as there’s a requisition that’s linked to it. Deleting requisition should be handled properly and this is currently even supported on the UI. The users are allowed to delete a requisition as long as it’s in initiated state and deleting it means that a new requisition needs to be initiated for that period. The remaining delete is for JasperTemplates and it doesn’t directly affect other entities.

For fulfillment service, deleting an Order may be problematic because ProofOfDelivery is linked with an Order that is required. Other delete endpoints include ProofOfDelivery, JasperTemplates and TransferProperties. I’d say there’s no problem with the templates and properties, but I’m not sure how much sense does it make to allow deleting proof of deliveries (even though they shouldn’t cause 500).

That being said I’d recommend deleting/marking as “not to be used” delete endpoints for requisitionTemplate and order. Endpoint for proofOfDelivery can be considered as well. Thoughts, component leads?

Also, how would you recommend marking the endpoints as “not to be used”?. Should we remove them from RAML completely? (Endpoints that are not present in RAML cannot be used at all and we don’t have to remove them from the codebase completely). A milder approach is just leaving a note in the description in RAML and possibly javadocs that they are not to be used, but that may be hardly noticeable for implementers.

Regards,

Sebastian.

Sebastian Brudziński

Software Developer / Team Leader

sbrudzinski@soldevelo.com

SolDevelo Sp. z o. o. [LLC]

Office: +48 58 782 45 40 / Fax: +48 58 782 45 41 Al. Zwycięstwa 96/98 81-451, Gdynia

http://www.soldevelo.com

Place of registration: Regional Court for the City of Gdansk KRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585, Share capital: 60,000.00 PLN

On 25.01.2017 22:05, Jake Watson wrote:

It also raises questions about RBAC, no?

Having a true delete (with referential integrity) is indeed useful assuming we have audit logging, but probably only for a very, very few users.

From: openlmis-dev@googlegroups.com on behalf of Josh Zamor josh.zamor@villagereach.org

Date: Wednesday, January 25, 2017 at 12:37 PM

To: Sebastian Brudziński sbrudzinski@soldevelo.com

Cc:openlmis-dev@googlegroups.comopenlmis-dev@googlegroups.com

Subject: Re: [openlmis-dev] Error handling for deletes causing constraint violations

Lets step back and be specific about certain endpoints.

DELETE /api/geographicZones/{id}

To answer the question:

“what happens to the facilities that are listed as being in the GeoGraphic zone, when we delete the zone?”

This should have been asked when we implemented the DELETE endpoint. Or when we associated the Facility with the GeographicZone. Right? Was it asked? I could expound on this however we all know the answer, and the answer is that we failed when implementing DELETE /api/geographicZone/{id}. If we didn’t ask the question, why was it even implemented? I can and I will define the current 500 error an indicator that we didn’t probe for and understand the requirements when we implemented the endpoint. I’ll also say I’m glad it does return a 500, as it’s catching a violation of a known requirement: facilities are associated with a geographic zone. Lets not remove that until we know what should be done.

Luckily we’re talking about an edge case of an administrative function, so if 3.0 ships with this we can mark it as our first minor bug and then finally ask the correct “business logic” question - what should be done if we “delete” a geographic zone that has facilities attached.

Lets get to that, however for now lets answer the following:

  • How many endpoints implement the DELETE operation?
  • How many of those endpoints implement DELETE like /api/geographicZones/{id} ? i.e. lead to 500 level responses?
  • Are there other endpoints, which implement DELETE, which likely wouldn’t lead to a 500 level response, which we’re concerned about? What about DELETE /api/requisitions/{id} ?

I did a quick survey of the Reference Data service and feel pretty comfortable saying that all 17 DELETE operations are suspect.

Regardless of the approach for deleting/archiving that we’ll eventually want, I’m pretty certain that 3.0 is going to ship at this point either with DELETE endpoints that hopefully will return HTTP 500 when a known business constraint is violated (e.g. a facility being associated), or we’ll decide to remove these endpoints and address the functional need in a later release.

For reference data, I’m leaning toward removing these endpoints / marking them as “not to be used”. Component leads, please answer these three questions for your service.

Thank you Sebastian for bringing this up. This is important to solve, though of course you can see I have concerns about how we ended up with all these DELETE endpoints - especially in reference data.

Best,

Josh

Josh Zamor|josh.zamor@villagereach.org

*Senior Software Development Engineer, OpenLMIS Architect

Village****Reach ** Starting at the Last Mile*

2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA

DIRECT: 1.206.512.1501 CELL: 1.847.504.7262 FAX: 1 206.860.6972

SKYPE: josh.zamor.vr

www.villagereach.org

Connect on Facebook, **Twitter ** and our** **Blog

On Jan 25, 2017, at 11:11 AM, Sebastian Brudziński sbrudzinski@soldevelo.com wrote:

Thanks for the answer Josh.

From what you are saying, the DELETE should be more of an “archive”, rather than actual remove from the system. Does that mean we do not want to support complete removal at all? I think that raises some more questions.

What does it mean that a facility is associated with a geographic zone that is marked as deleted? Does this mean that the facility does no longer has got that association? If so, it would be simpler to just remove all non-null constraints and deletes would work just fine as well. However, I believe we actually want to have those checks in place? Or would we treat them as an association that still exists but with an archived resource? If so, do we still consider that association valid? Is it kind of - it doesn’t matter that this resource is archived now, it existsed in the past so it’s OK?

About discovering the resource via “/search”, this is probably correct when creating new resources. However all the existing instances will simply look for a resource using UUIDs (especially cross-service relationships). In that case they would just fetch the “archived” resources they are linked to. Is that what we want to happen for all of our cross-service associations?

From what I understand, this approach means revisiting all our searches and introducing the checks for the deleted/archived flag + an option to search for archived resources. We will probably need to revisit our unique constraints as well (we may have duplications if we keep archived instances). Is that refactor something that we want to do in 3.0 for all of our services?

Best regards,

Sebastian.

<Soldevelo_logo_EPS_CMYK.png>

Sebastian Brudziński

Software Developer / Team Leader

sbrudzinski@soldevelo.com

SolDevelo Sp. z o. o. [LLC]

Office: +48 58 782 45 40/ Fax: +48 58 782 45 41Al. Zwycięstwa 96/9881-451, Gdynia

http://www.soldevelo.com

Place of registration: Regional Court for the City of GdanskKRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,Share capital: 60,000.00 PLN

On 25.01.2017 18:44, Josh Zamor wrote:

Hi Sebastian. I’m of the opinion that in general DELETE shouldn’t delete anything, but rather mark it as deleted/archived.

There are some simple positives:

  • we don’t lose data
  • we don’t have the referential integrity issue you described within a service
  • AND any client to a service that does a GET /resource/{id} will always return the resource once it’s created - that is it wouldn’t return a 200 on day 1, and on day 2 after it’s deleted suddenly return a 404 to the client. More on this later…

Some cons:

  • since we never delete, we always accumulate data. But DELETE is IMO an edge case and storage is cheap.
  • We haven’t followed this approach so far, however… It’s true that clients of a GET /resource/{id} might not be looking for some “archived” flag of the resource returned and this change would need to be accounted for. However that change I think is more modest when we think about how clients discover a resource: search. For most of our clients, they’ll be using some search endpoint first to find the resources before the first GET /resource/{id} is ever used. The convention for a search endpoint therefore could be that archived resource items are not returned, unless a query parameter (e.g. ?archived=true) is given.

Leaving it as a 500 error isn’t something we should do. Thoughts?

Best,

Josh

Josh Zamor|josh.zamor@villagereach.org

*Senior Software Development Engineer, OpenLMIS Architect

Village****Reach ** Starting at the Last Mile*

2900 Eastlake Ave. E, Suite 230, Seattle, WA 98102, USA

DIRECT: 1.206.512.1501 CELL: 1.847.504.7262 FAX: 1 206.860.6972

SKYPE: josh.zamor.vr

www.villagereach.org

Connect on Facebook, **Twitter ** and our** **Blog

On Jan 25, 2017, at 7:58 AM, Sebastian Brudziński sbrudzinski@soldevelo.com wrote:

Hello everyone,

Today I’ve been investigating OLMIS-1705 that talks about DELETE endpoints not working. The problem with the delete is that it may affect the not-null constraint in another entity, in which case the resulting SQL query will fail. This is wrapped into the DataIntegrityViolationExceptio n. As an example, attempting to delete a geographic zone will fail if there’s any facility that is currently linked to that geographic zone (because Facility requires non-null geographic zone).

I’ve been wondering about the approach we want to take to handle those errors. What currently happens is that the delete throws DataIntegrityViolationExceptio n, which is translated into 500 error code. We could try to implement manual validations before the actual delete happens, but we’ve got quite a lot of the not-null constraints scattered across the services and having to check all of the instances of let’s say 10 entities upon delete somehow doesn’t seem like a good solution. Any ideas for other approaches to tackle this? Or should we just leave it be 500 error code?

Best regards,

Sebastian.

<Soldevelo_logo_EPS_CMYK.png>

Sebastian Brudziński

Software Developer / Team Leader

sbrudzinski@soldevelo.com

SolDevelo Sp. z o. o. [LLC]

Office: +48 58 782 45 40/ Fax: +48 58 782 45 41Al. Zwycięstwa 96/9881-451, Gdynia

http://www.soldevelo.com

Place of registration: Regional Court for the City of GdanskKRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,Share capital: 60,000.00 PLN

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/5888CB0B.6050305%40soldevelo.com
.

For more options, visit https://groups.google.com/d/optout.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/BDB8ACA4-6F39-4FF7-90F3-F7AE23B39214%40villagereach.org
.

For more options, visit https://groups.google.com/d/optout.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/a620c1fb-56b0-fb4e-e69d-318a048aab05%40soldevelo.com
.

For more options, visit https://groups.google.com/d/optout.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/765B70FF-DC5A-4829-A518-6F3DBD4EA230%40villagereach.org
.

For more options, visit https://groups.google.com/d/optout.

You received this message because you are subscribed to the Google Groups “OpenLMIS Dev” group.

To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

To post to this group, send email to
openlmis-dev@googlegroups.com.

To view this discussion on the web visit
https://groups.google.com/d/msgid/openlmis-dev/588B5C04.10403%40soldevelo.com
.

For more options, visit https://groups.google.com/d/optout.

Thanks for the answers,

checking for requisitions that may be using the requisition templates sounds good. Brandon, this should not affect what we have done in OLMIS-1360, since in the first place we will be checking whether the template is in use (associated with a requisition) and if it is - we won't touch it. So this won't have any effect on this ticket, but we will of course make sure to verify that. Josh, I'd personally choose less restrictive approach, that is having the DELETE endpoints working and only annotating that they completely remove the instance and should be used with care or not used at all. Since we have the constraints in place, the instance will not be deleted if that would break any required associations. I don't think that someone would accidentally issue a DELETE request and it may turn out that deleting something is crucial for some case, but not possible.

Regards,

Sebastian.

···

sbrudzinski@soldevelo.com
On 31.01.2017 07:30, Josh Zamor wrote:

    I share Brandon's thoughts on the Requisition Template.



    Thanks Sebastian for tracking those down.  I think from that list, we'll proceed by treating those endpoints separately from the DELETE endpoints of Reference Data.



    For Reference Data, which appears to have by far the most DELETE endpoints, I'd be fine with simply adding to the RAML not to use the DELETE operation for each.  However I could also see a value to removing access to the endpoint altogether to safe guard against accidental usage.  Sebastian, since SolDevelo will be the first organization to implement 3.0, I'd like to hear your opinion:  Would you prefer to remove them to be safe, or can we do the least-effort and simply recommend against their usage in the API documentation?



    Best,

    Josh



    On Monday, January 30, 2017 at 5:01:37 PM UTC-8, brandon.bowersox-johnson wrote:

Hi Sebastian,

          Regarding the RequisitionTemplate, I want to add one more thing: Adding this simple DELETE ability on a RequisitionTemplate should not change what you folks did in ticket OLMIS-1360*. Specifically, that ticket means that each time a requisition is created, it uses the current template at that time. Even if the template changes later, that does not break the existing requisitions.
          I mention that here because hopefully even if a Requisition Template that has existing Requisitions gets deleted, that probably should not break the Requisition any way.
          In summary, for the RequisitionTemplate DELETE endpoint, I will be happy if we do the simple solution for deleting that I suggested in my previous message below, AND as long as we make sure 1360 still works.

-Brandon

        On Sunday, January 29, 2017 at 3:00:42 PM UTC-8, brandon.bowersox-johnson wrote:
                  Hi Sebastian and Josh,
                  For the RequisitionTemplate DELETE endpoint, I suggest we simply prohibit deletion of any RequisitionTemplate that has 1 or more Requisitions. That should involve adding a simple check before deletion and responding with a 400 status code* and an error message.
                  For RequisitionTemplate I don’t think we need to do the complex thing of marking a deleted item as “Archived”. If there are zero Requisitions, then we truly allow the user to delete the RequisitionTemplate altogether, and that is sufficient. This is not one of our high-value endpoints where we want to archive old records or store a long audit trail. I don’t think an “Archived” status for RequisitionTemplate data is worth the complexity.
                  *HTTP 422 could also appropriate. OpenLMIS service style guide points to this: [
                    http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#http-status](http://www.vinaysahni.com/best-practices-for-a-pragmatic-restful-api#http-status)
                  . I don’t think we have any consistency yet about what to return when DELETEs are not allowed. So I’m fine with 400 or 422.

Sebastian , this ticket 1705 seems to touch a lot of components and it has a lot of different DELETE endpoints to address. If we should split the ticket to address different services/components on their own, that’s fine. Also, if it would help to have a Skype meeting to speed up our decision-making, feel free to schedule.

-Brandon

** From:
** <

                I've taken a look at the requisition and fulfillment services. The requisition service has currently got 3 DELETE endpoints and the fulfillment service has got 4 of them. It looks like both of them have got only 1 endpoint that may be problematic (that is result in 500 error).



                For requisition service this is RequisitionTemplate. Requisition is linked to a RequisitionTemplate and it cannot be deleted as long as there's a requisition that's linked to it. Deleting requisition should be handled properly and this is currently even supported on the UI. The users are allowed to delete a requisition as long as it's in initiated state and deleting it means that a new requisition needs to be initiated for that period. The remaining delete is for JasperTemplates and it doesn't directly affect other entities.



                For fulfillment service, deleting an Order may be problematic because ProofOfDelivery is linked with an Order that is required. Other delete endpoints include ProofOfDelivery, JasperTemplates and TransferProperties. I'd say there's no problem with the templates and properties, but I'm not sure how much sense does it make to allow deleting proof of deliveries (even though they shouldn't cause 500).



                That being said I'd recommend deleting/marking as "not to be used" delete endpoints for requisitionTemplate and order. Endpoint for proofOfDelivery can be considered as well. Thoughts, component leads?



                Also, how would you recommend marking the endpoints as "not to be used"?. Should we remove them from RAML completely? (Endpoints that are not present in RAML cannot be used at all and we don't have to remove them from the codebase completely). A milder approach is just leaving a note in the description in RAML and possibly javadocs that they are not to be used, but that may be hardly noticeable for implementers.



                Regards,

                Sebastian.



                --

Sebastian Brudziński

                  Software Developer / Team Leader

                  sbrudzinski@soldevelo.com

SolDevelo Sp. z o. o. [LLC]

                  Office: +48 58 782 45 40 / Fax: +48 58 782 45 41 Al. Zwycięstwa 96/98 81-451, Gdynia

                  [http://www.soldevelo.com](http://www.SolDevelo.com)



                  Place of registration: Regional Court for the City of Gdansk KRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585, Share capital: 60,000.00 PLN
                  On 25.01.2017 22:05, Jake Watson wrote:
                        It also raises questions about RBAC, no?
                        Having a true delete (with referential integrity) is indeed useful assuming we have audit logging, but probably only for a very, very few users.

** From:** <

                        Lets step back and be specific about certain endpoints.
                          DELETE /api/geographicZones/{id}
                          To answer the question:
                          “what happens to the facilities that are listed as being in the GeoGraphic zone, when we delete the zone?”
                          This should have been asked when we implemented the DELETE endpoint.  Or when we associated the Facility with the GeographicZone.  Right?  Was it asked?  I could expound on this however we all know the answer, and the answer is that we failed when implementing DELETE /api/geographicZone/{id}.  If we didn’t ask the question, why was it even implemented?  I can and I will define the current 500 error an indicator that we didn’t probe for and understand the requirements when we implemented the endpoint.  I’ll also say I’m glad it does return a 500, as it’s catching a violation of a known requirement:  facilities are associated with a geographic zone.  Lets not remove that until we know what should be done.
                          Luckily we’re talking about an edge case of an administrative function, so if 3.0 ships with this we can mark it as our first minor bug and then finally ask the correct “business logic” question  - what should be done if we “delete” a geographic zone that has facilities attached.
                          Lets get to that, however for now lets answer the following:
  •                             How many endpoints implement the DELETE operation?
    
  •                             How many of those endpoints implement DELETE like /api/geographicZones/{id} ?  i.e. lead to 500 level responses?
    
  •                             Are there other endpoints, which implement DELETE, which likely wouldn’t lead to a 500 level response, which we’re concerned about?  What about DELETE /api/requisitions/{id} ?
    
                            I did a quick survey of the Reference Data service and feel pretty comfortable saying that all 17 DELETE operations are suspect.
                          Regardless of the approach for deleting/archiving that we’ll eventually want, I’m pretty certain that 3.0 is going to ship at this point either with DELETE endpoints that hopefully will return HTTP 500 when a known business constraint is violated (e.g. a facility being associated), or we’ll decide to remove these endpoints and address the functional need in a later release.
                          For reference data, I’m leaning toward removing these endpoints / marking them as “not to be used”.  Component leads, please answer these three questions for your service.
                          Thank you Sebastian for bringing this up.  This is important to solve, though of course you can see I have concerns about how we ended up with all these DELETE endpoints - especially in reference data.

Best,

Josh

                                        Josh Zamor|josh.zamor@villagereach.org 

                                      *                                              Senior Software Development Engineer, OpenLMIS Architect 



                                       * [**Village****Reach**](http://villagereach.org/)* **                                              Starting at the Last Mile* 

                                                                                  2900 Eastlake Ave. E, Suite 230,  Seattle, WA 98102, USA

                                        DIRECT: 1.206.512.1501  CELL: 1.847.504.7262   FAX: 1 206.860.6972

                                        SKYPE: josh.zamor.vr

                                        [www.villagereach.org](http://www.villagereach.org/)


                                        Connect on **[Facebook](https://www.facebook.com/pages/VillageReach/103205113922),** **[Twitter](https://twitter.com/VillageReach) **and our[** ****Blog** ](http://villagereach.org/see-our-blog/thoughts-from-the-last-mile/)
                                    On Jan 25, 2017, at 11:11 AM, Sebastian Brudziński <                                        > wrote:
                                      Thanks for the answer Josh.
                                      From what you are saying, the DELETE should be more of an "archive", rather than actual remove from the system. Does that mean we do not want to support complete removal at all? I think that raises some more questions.
                                      What does it mean that a facility is associated with a geographic zone that is marked as deleted? Does this mean that the facility does no longer has got that association? If so, it would be simpler to just remove all non-null constraints and deletes would work just fine as well. However, I believe we actually want to have those checks in place? Or would we treat them as an association that still exists but with an archived resource? If so, do we still consider that association valid? Is it kind of - it doesn't matter that this resource is archived now, it existsed in the past so it's OK?
                                      About discovering the resource via "/search", this is probably correct when creating new resources. However all the existing instances will simply look for a resource using UUIDs (especially cross-service relationships). In that case they would just fetch the "archived" resources they are linked to. Is that what we want to happen for all of our cross-service associations?
                                      From what I understand, this approach means revisiting all our searches and introducing the checks for the deleted/archived flag + an option to search for archived resources. We will probably need to revisit our unique constraints as well (we may have duplications if we keep archived instances). Is that refactor something that we want to do in 3.0 for all of our services?
                                      Best regards,

                                      Sebastian.

<Soldevelo_logo_EPS_CMYK.png>

** Sebastian Brudziński**

                                        Software Developer / Team Leader

                                        sbrudzinski@soldevelo.com

** SolDevelo Sp. z o. o. [LLC]**

                                        Office: +48 58 782 45 40/ Fax: +48 58 782 45 41Al. Zwycięstwa 96/9881-451, Gdynia

                                        [http://www.soldevelo.com](http://www.soldevelo.com/)



                                        Place of registration: Regional Court for the City of GdanskKRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,Share capital: 60,000.00 PLN
                                        On 25.01.2017 18:44, Josh Zamor wrote:
                                        Hi Sebastian.  I’m of the opinion that in general DELETE shouldn’t delete anything, but rather mark it as deleted/archived.
                                          There are some simple positives:
                                          - we don’t lose data
                                          - we don’t have the referential integrity issue you described within a service
                                          - AND any client to a service that does a GET /resource/{id} will always return the resource once it’s created - that is it wouldn’t return a 200 on day 1, and on day 2 after it’s deleted suddenly return a 404 to the client.  More on this later…
                                          Some cons:
                                          - since we never delete, we always accumulate data.  But DELETE is IMO an edge case and storage is cheap.
                                          - We haven’t followed this approach so far, however…  It’s true that clients of a GET /resource/{id} might not be looking for some “archived” flag of the resource returned and this change would need to be accounted for.  However that change I think is more modest when we think about how clients discover a resource: search.  For most of our clients, they’ll be using some search endpoint first to find the resources before the first GET /resource/{id} is ever used.  The convention for a search endpoint therefore could be that archived resource items are not returned, unless a query parameter (e.g. ?archived=true) is given.
                                            Leaving it as a 500 error isn’t something we should do.  Thoughts?

Best,

Josh

Josh Zamor|josh.zamor@villagereach.org

                                                    *                                                          Senior Software Development Engineer, OpenLMIS Architect 



                                                     * [**Village****Reach**](http://villagereach.org/)* **                                                          Starting at the Last Mile* 

                                                                                                              2900 Eastlake Ave. E, Suite 230,  Seattle, WA 98102, USA

                                                      DIRECT: 1.206.512.1501  CELL: 1.847.504.7262   FAX: 1 206.860.6972

                                                      SKYPE: josh.zamor.vr

                                                      [](http://www.villagereach.org)
                                                      Connect on**[Facebook](https://www.facebook.com/pages/VillageReach/103205113922),** **[Twitter](https://twitter.com/VillageReach) **and our[** ****Blog** ](http://villagereach.org/see-our-blog/thoughts-from-the-last-mile/)
                                                  On Jan 25, 2017, at 7:58 AM, Sebastian Brudziński <                                                      > wrote:

Hello everyone,

                                                    Today I've been investigating OLMIS-1705 that talks about DELETE endpoints not working. The problem with the delete is that it may affect the not-null constraint in another entity, in which case the resulting SQL query will fail. This is wrapped into the DataIntegrityViolationExceptio                                                        n. As an example, attempting to delete a geographic zone will fail if there's any facility that is currently linked to that geographic zone (because Facility requires non-null geographic zone).



                                                    I've been wondering about the approach we want to take to handle those errors. What currently happens is that the delete throws DataIntegrityViolationExceptio                                                        n, which is translated into 500 error code. We could try to implement manual validations before the actual delete happens, but we've got quite a lot of the not-null constraints scattered across the services and having to check all of the instances of let's say 10 entities upon delete somehow doesn't seem like a good solution. Any ideas for other approaches to tackle this? Or should we just leave it be 500 error code?



                                                    Best regards,

                                                    Sebastian.

<Soldevelo_logo_EPS_CMYK.png>

Sebastian Brudziński

                                                      Software Developer / Team Leader

** SolDevelo Sp. z o. o. [LLC]**

                                                      Office: +48 58 782 45 40/ Fax: +48 58 782 45 41Al. Zwycięstwa 96/9881-451, Gdynia

                                                      [](http://www.soldevelo.com)

                                                      Place of registration: Regional Court for the City of GdanskKRS: 0000332728, TAX ID: PL5862240331, REGON: 220828585,Share capital: 60,000.00 PLN
                                                  --

                                                  You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.

                                                  To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

                                                  To post to this group, send email to
                                                  . To view this discussion on the web visit

                                        You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.

                                        To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

                                        To post to this group, send email to
                                        . To view this discussion on the web visit

                                    You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.

                                    To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

                                    To post to this group, send email to
                                                                              . To view this discussion on the web visit

                        You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.

                        To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

                        To post to this group, send email to
                                                      . To view this discussion on the web visit

                You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.

                To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

                To post to this group, send email to
                                      . To view this discussion on the web visit

  You received this message because you are subscribed to the Google Groups "OpenLMIS Dev" group.

  To unsubscribe from this group and stop receiving emails from it, send an email to openlmis-dev+unsubscribe@googlegroups.com.

  To post to this group, send email to openlmis-dev@googlegroups.com.

  To view this discussion on the web visit [](https://groups.google.com/d/msgid/openlmis-dev/166a251c-ddbd-4493-a637-331e01a811e1%40googlegroups.com?utm_medium=email&utm_source=footer)      . For more options, visit .

openlmis-dev@googlegroups.com > on behalf of Sebastian Brudziński < > Friday, January 27, 2017 at 6:41 AM "sbrudzinski@soldevelo.com
Date:
To: openlmis-dev@googlegroups.com " openlmis-dev@googlegroups.com

                    **Subject:**                         Re: [openlmis-dev] Error handling for deletes causing constraint violationsopenlmis-dev@googlegroups.com                          > on behalf of Josh Zamor <                          > Wednesday, January 25, 2017 at 12:37 PM Sebastian Brudziński <> "josh.zamor@villagereach.org

Date:
To: sbrudzinski@soldevelo.com
Cc: openlmis-dev@googlegroups.com " openlmis-dev@googlegroups.com

                      **Subject:**                           Re: [openlmis-dev] Error handling for deletes causing constraint violationssbrudzinski@soldevelo.com[www.villagereach.org](http://www.villagereach.org)

sbrudzinski@soldevelo.comsbrudzinski@soldevelo.comhttp://www.soldevelo.com

openlmis-dev@googlegroups.com

https://groups.google.com/d/msgid/openlmis-dev/5888CB0B.6050305%40soldevelo.com.

                                                  For more options, visit [](https://groups.google.com/d/optout)[https://groups.google.com/d/](https://groups.google.com/d/)optout.openlmis-dev@googlegroups.com


https://groups.google.com/d/msgid/openlmis-dev/BDB8ACA4-6F39-4FF7-90F3-F7AE23B39214%40villagereach.org.

                                        For more options, visit [](https://groups.google.com/d/optout)[https://groups.google.com/d/](https://groups.google.com/d/)optout.openlmis-dev@googlegroups.com


https://groups.google.com/d/msgid/openlmis-dev/a620c1fb-56b0-fb4e-e69d-318a048aab05%40soldevelo.com.

                                    For more options, visit [](https://groups.google.com/d/optout)[https://groups.google.com/d/](https://groups.google.com/d/)optout.openlmis-dev@googlegroups.com


https://groups.google.com/d/msgid/openlmis-dev/765B70FF-DC5A-4829-A518-6F3DBD4EA230%40villagereach.org.

                        For more options, visit [](https://groups.google.com/d/optout)[https://groups.google.com/d/](https://groups.google.com/d/)optout.openlmis-dev@googlegroups.com


https://groups.google.com/d/msgid/openlmis-dev/588B5C04.10403%40soldevelo.com.

                For more options, visit [https://groups.google.com/d/optout](https://groups.google.com/d/optout).

              [https://groups.google.com/d/msgid/openlmis-dev/166a251c-ddbd-4493-a637-331e01a811e1%40googlegroups.com](https://groups.google.com/d/msgid/openlmis-dev/166a251c-ddbd-4493-a637-331e01a811e1%40googlegroups.com)

https://groups.google.com/d/optout