Skip to content

Commit

Permalink
feat: adds RecommendationClient
Browse files Browse the repository at this point in the history
[changelog]

The personalization strategy endpoint is migrating from the Search API
to the Recommendation API.

To use the Recommendation API, one must now use Recommendation methods instead
of Insights one.
  • Loading branch information
Ant-hem authored and aseure committed Jan 24, 2020
1 parent 9adab9f commit b92ba38
Show file tree
Hide file tree
Showing 7 changed files with 183 additions and 7 deletions.
13 changes: 13 additions & 0 deletions src/main/scala/algolia/AlgoliaClient.scala
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ class AlgoliaClient(applicationId: String,

val analyticsHost: String = "https://analytics.algolia.com"
val insightsHost: String = "https://insights.algolia.io"
/* Recommendation default host is set as 'var' because the region might be overridden. */
var recommendationHost: String = "https://recommendation.us.algolia.com"

val userAgent =
s"Algolia for Scala (${BuildInfo.version}); JVM (${System.getProperty("java.version")}); Scala (${BuildInfo.scalaVersion})"
Expand Down Expand Up @@ -151,6 +153,8 @@ class AlgoliaClient(applicationId: String,
requestAnalytics(payload)
} else if (payload.isInsights) {
requestInsights(payload)
} else if (payload.isRecommendation) {
requestRecommendation(payload)
} else {
requestSearch(payload)
}
Expand All @@ -165,6 +169,15 @@ class AlgoliaClient(applicationId: String,
}
}

private[algolia] def requestRecommendation[T: Manifest](payload: HttpPayload)(
implicit executor: ExecutionContext): Future[T] = {
httpClient.request[T](recommendationHost, headers, payload).andThen {
case Failure(e) =>
logger.debug("Recommendation API call failed", e)
Future.failed(new AlgoliaClientException("Recommendation API call failed", e))
}
}

private[algolia] def requestInsights[T: Manifest](payload: HttpPayload)(
implicit executor: ExecutionContext): Future[T] = {
httpClient.request[T](insightsHost, headers, payload).andThen {
Expand Down
75 changes: 75 additions & 0 deletions src/main/scala/algolia/definitions/RecommendationDefinition.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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.http.{GET, HttpPayload, POST}
import algolia.objects.{RequestOptions, SetStrategyRequest}
import org.json4s.Formats
import org.json4s.native.Serialization.write

case class GetRecommendationStrategyDefinition(requestOptions: Option[RequestOptions] = None)(
implicit val formats: Formats)
extends Definition {

override type T = GetRecommendationStrategyDefinition

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

override private[algolia] def build(): HttpPayload = {
HttpPayload(
GET,
Seq("1", "strategies", "personalization"),
isSearch = false,
isRecommendation = true,
requestOptions = requestOptions
)
}

}

case class SetRecommendationStrategyDefinition(
s: SetStrategyRequest,
requestOptions: Option[RequestOptions] = None)(implicit val formats: Formats)
extends Definition {

override type T = SetRecommendationStrategyDefinition

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

override private[algolia] def build(): HttpPayload = {
HttpPayload(
POST,
Seq("1", "strategies", "personalization"),
body = Some(write(s)),
isSearch = false,
isRecommendation = true,
requestOptions = requestOptions
)
}

}
12 changes: 11 additions & 1 deletion src/main/scala/algolia/dsl/GetDsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ package algolia.dsl
import algolia.AlgoliaDsl.ABTests
import algolia.definitions._
import algolia.objects.Strategy
import algolia.responses.{GetObject, Results, TopUserID, UserDataWithCluster}
import algolia.responses.{GetObject, GetStrategyResponse, Results, TopUserID, UserDataWithCluster}
import algolia.{AlgoliaClient, Executable}
import org.json4s.Formats
import org.json4s.JsonAST.JObject
Expand Down Expand Up @@ -73,6 +73,8 @@ trait GetDsl {
"1.34")
def personalizationStrategy() = GetPersonalizationStrategyDefinition()

def personalizationRecommendationStrategy() = GetRecommendationStrategyDefinition()

}

implicit object GetObjectDefinitionExecutable
Expand Down Expand Up @@ -121,4 +123,12 @@ trait GetDsl {
}
}

implicit object GetPersonalizationRecommendationStrategy
extends Executable[GetRecommendationStrategyDefinition, GetStrategyResponse] {
override def apply(client: AlgoliaClient, query: GetRecommendationStrategyDefinition)(
implicit executor: ExecutionContext): Future[GetStrategyResponse] = {
client.request[GetStrategyResponse](query.build())
}
}

}
21 changes: 15 additions & 6 deletions src/main/scala/algolia/dsl/SetDsl.scala
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,13 @@

package algolia.dsl

import algolia.{AlgoliaClient, Executable}
import algolia.definitions.{
GetPersonalizationStrategyDefinition,
SetPersonalizationStrategyDefinition
SetPersonalizationStrategyDefinition,
SetRecommendationStrategyDefinition
}
import algolia.inputs._
import algolia.objects.Strategy
import algolia.responses.SetStrategyResult
import algolia.objects.{SetStrategyRequest, Strategy}
import algolia.responses.{SetStrategyResponse, SetStrategyResult}
import algolia.{AlgoliaClient, Executable}
import org.json4s.Formats

import scala.concurrent.{ExecutionContext, Future}
Expand All @@ -48,6 +47,9 @@ trait SetDsl {
def personalizationStrategy(s: Strategy): SetPersonalizationStrategyDefinition =
SetPersonalizationStrategyDefinition(s)

def personalizationRecommendationStrategy(
strategy: SetStrategyRequest): SetRecommendationStrategyDefinition =
SetRecommendationStrategyDefinition(strategy)
}

@deprecated(
Expand All @@ -61,4 +63,11 @@ trait SetDsl {
}
}

implicit object SetPersonalizationRecommendationStrategy
extends Executable[SetRecommendationStrategyDefinition, SetStrategyResponse] {
override def apply(client: AlgoliaClient, query: SetRecommendationStrategyDefinition)(
implicit executor: ExecutionContext): Future[SetStrategyResponse] = {
client.request[SetStrategyResponse](query.build())
}
}
}
1 change: 1 addition & 0 deletions src/main/scala/algolia/http/HttpPayload.scala
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ private[algolia] case class HttpPayload(verb: HttpVerb,
isSearch: Boolean,
isAnalytics: Boolean = false,
isInsights: Boolean = false,
isRecommendation: Boolean = false,
requestOptions: Option[RequestOptions]) {

def apply(host: String,
Expand Down
34 changes: 34 additions & 0 deletions src/main/scala/algolia/objects/Recommendation.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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.objects

case class FacetsScoring(facetName: String, score: Int)

case class EventsScoring(eventName: String, eventType: String, score: Int)

case class SetStrategyRequest(eventsScoring: Seq[EventsScoring],
facetsScoring: Seq[FacetsScoring],
personalizationImpact: Int)
34 changes: 34 additions & 0 deletions src/main/scala/algolia/responses/Recommendation.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* 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.responses

import algolia.objects.{EventsScoring, FacetsScoring}

case class SetStrategyResponse(status: Int, message: String)

case class GetStrategyResponse(eventsScoring: Option[Seq[EventsScoring]],
facetsScoring: Option[Seq[FacetsScoring]],
personalizationImpact: Option[Int])

0 comments on commit b92ba38

Please sign in to comment.