Skip to content

Commit

Permalink
Fixed: Introduce 'delete objectFromIndex X' to deprecate 'delete obje…
Browse files Browse the repository at this point in the history
…ct X from index Y'

As we recently discovered, the following request was able to delete an
entire index:

```
delete from "index" `object` ""
```

To fix this issue, we deprecated this way of deleting the object with
the following approach:

```
val op: Option[SafeDeleteObjectOperation] = SafeDeleteObjectOperation("index", "objectID")
delete objectFromIndex op.get
```

which forces the user to pass non-empty index name and objectID,
otherwise the returned `Option` is `None`. This is made possible by the
use of private constructors on the `SafeDeleteObjectDefinition` and of a
companion object with a public `apply()` method that only lets the user
instantiate a `SafeDeleteObjectDefinition` with valid i.e. non-empty
parameters.
  • Loading branch information
aseure committed Oct 31, 2018
1 parent 9574ab4 commit 3c37724
Show file tree
Hide file tree
Showing 8 changed files with 160 additions and 12 deletions.
3 changes: 3 additions & 0 deletions src/main/scala/algolia/definitions/BatchDefinition.scala
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ case class BatchDefinition(
case DeleteObjectDefinition(Some(index), Some(oid), _) =>
Traversable(DeleteObjectOperation(index, oid))

case SafeDeleteObjectDefinition(op, _) =>
Traversable(DeleteObjectOperation(op.index, op.objectID))

case DeleteIndexDefinition(index, _) =>
Traversable(DeleteIndexOperation(index))

Expand Down
2 changes: 2 additions & 0 deletions src/main/scala/algolia/definitions/DeleteDefinition.scala
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ case class DeleteObjectDefinition(

def from(ind: String): DeleteObjectDefinition = copy(index = Some(ind))

@deprecated("use objectFromIndex", "1.30.0")
def index(ind: String): DeleteObjectDefinition = copy(index = Some(ind))

@deprecated("use objectFromIndex", "1.30.0")
def objectId(objectId: String): DeleteObjectDefinition =
copy(oid = Some(objectId))

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016 Algolia
* http://www.algolia.com/
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package algolia.definitions

import algolia._
import algolia.http.HttpPayload
import algolia.inputs.SafeDeleteObjectOperation
import algolia.objects.RequestOptions
import org.json4s.Formats

case class SafeDeleteObjectDefinition(
op: SafeDeleteObjectOperation,
requestOptions: Option[RequestOptions] = None)(implicit val formats: Formats)
extends Definition {

override type T = SafeDeleteObjectDefinition

override def options(requestOptions: RequestOptions): SafeDeleteObjectDefinition =
copy(requestOptions = Some(requestOptions))

override private[algolia] def build(): HttpPayload =
HttpPayload(
http.DELETE,
Seq("1", "indexes", op.index, op.objectID),
isSearch = false,
requestOptions = requestOptions
)
}
6 changes: 5 additions & 1 deletion src/main/scala/algolia/dsl/DeleteDsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
package algolia.dsl

import algolia.definitions._
import algolia.inputs.SafeDeleteObjectOperation
import algolia.responses.Task
import algolia.{AlgoliaClient, Executable}
import org.json4s.Formats
Expand All @@ -43,10 +44,13 @@ trait DeleteDsl {
DeleteIndexDefinition(index)

//Object
@deprecated("use objectFromIndex", "1.30.0")
def objectId(objectId: String) =
DeleteObjectDefinition(oid = Some(objectId))

//Object
def objectFromIndex(op: SafeDeleteObjectOperation) = SafeDeleteObjectDefinition(op)

//Object(s)
def from(index: String) = DeleteObjectDefinition(index = Some(index))

def key(keyName: String) = DeleteKeyDefinition(keyName)
Expand Down
44 changes: 44 additions & 0 deletions src/main/scala/algolia/inputs/SafeDeleteObjectOperation.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016 Algolia
* http://www.algolia.com/
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package algolia.inputs

class SafeDeleteObjectOperation private (i: String, o: String) {
def index: String = i
def objectID: String = o
}

object SafeDeleteObjectOperation {
def apply(index: String, objectID: String): Option[SafeDeleteObjectOperation] = {
val trimmedIndex = index.trim
val trimmedObjectID = objectID.trim

if (trimmedIndex.isEmpty || trimmedObjectID.isEmpty) {
None
} else {
Some(new SafeDeleteObjectOperation(trimmedIndex, trimmedObjectID))
}
}
}
9 changes: 5 additions & 4 deletions src/test/scala/algolia/dsl/BatchTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ package algolia.dsl
import algolia.AlgoliaDsl._
import algolia.AlgoliaTest
import algolia.http.{HttpPayload, POST}
import algolia.inputs.SafeDeleteObjectOperation

class BatchTest extends AlgoliaTest {

Expand Down Expand Up @@ -171,15 +172,15 @@ class BatchTest extends AlgoliaTest {

it("should deletes multiple objects") {
batch(
delete from "test1" objectId "1",
delete from "test2" objectId "2"
delete objectFromIndex SafeDeleteObjectOperation("test1", "1").get,
delete objectFromIndex SafeDeleteObjectOperation("test2", "2").get
)
}

it("should call the API") {
val build = batch(
delete from "test1" objectId "1",
delete from "test2" objectId "2"
delete objectFromIndex SafeDeleteObjectOperation("test1", "1").get,
delete objectFromIndex SafeDeleteObjectOperation("test2", "2").get
).build()

val body = """
Expand Down
11 changes: 4 additions & 7 deletions src/test/scala/algolia/dsl/DeleteObjectTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,20 @@
package algolia.dsl

import algolia.AlgoliaDsl._
import algolia.AlgoliaTest
import algolia.{AlgoliaTest, inputs}
import algolia.http.{DELETE, HttpPayload, POST}
import algolia.inputs.SafeDeleteObjectOperation

class DeleteObjectTest extends AlgoliaTest {

describe("delete") {

it("deletes object") {
delete from "toto" objectId "oid"
}

it("deletes object with inverse DSL") {
delete objectId "oid" from "toto"
delete objectFromIndex SafeDeleteObjectOperation("toto", "oid").get
}

it("should call API") {
(delete from "toto" objectId "oid").build() should be(
(delete objectFromIndex SafeDeleteObjectOperation("toto", "oid").get).build() should be(
HttpPayload(
DELETE,
Seq("1", "indexes", "toto", "oid"),
Expand Down
46 changes: 46 additions & 0 deletions src/test/scala/algolia/inputs/SafeDeleteObjectOperationTest.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* The MIT License (MIT)
*
* Copyright (c) 2016 Algolia
* http://www.algolia.com/
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

package algolia.inputs
import algolia.AlgoliaTest

class SafeDeleteObjectOperationTest extends AlgoliaTest {

it("should produce a valid SafeDeleteObjectOperation with non-empty parameters") {
val op = SafeDeleteObjectOperation("index1", "objectID1")
op should not be (None)
op.get.index should be("index1")
op.get.objectID should be("objectID1")
}

it("should not produce a valid SafeDeleteObjectOperation with empty parameters") {
SafeDeleteObjectOperation("", "") should be(None)
SafeDeleteObjectOperation("not empty", "") should be(None)
SafeDeleteObjectOperation("", "not empty") should be(None)
SafeDeleteObjectOperation(" ", "not empty") should be(None)
SafeDeleteObjectOperation("not empty", " ") should be(None)
}

}

0 comments on commit 3c37724

Please sign in to comment.