Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/create an omnisearch #841

Closed
wants to merge 126 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
094d796
fix#769 : Omnisearch of entities only for default tenant
helakaraa Jun 3, 2024
a5f5218
fix#769 :add useParams tenant
helakaraa Jun 3, 2024
baa488f
fix#769 :add script view search for tenants
helakaraa Jun 4, 2024
59e0877
fix#769 : modify search field to k command search button
helakaraa Jun 12, 2024
fd7d284
fix #823 : refresh user table after editing a user
helakaraa May 29, 2024
af220dc
Fixes #821
baudelotphilippe Jun 7, 2024
80a15c6
improve rwd
baudelotphilippe Jun 7, 2024
c1d9638
space missing
baudelotphilippe Jun 7, 2024
e576250
improve rwd scroll on table
baudelotphilippe Jun 7, 2024
74c74c9
fix dropdown display after overflow:scroll
baudelotphilippe Jun 7, 2024
1eb1146
Tooltips content fix
baudelotphilippe Jun 7, 2024
0877d86
feat: improve OIDC
ptitFicus Jun 10, 2024
9256ed1
Update dev documentation
Jun 10, 2024
589e4d5
Setting version to 2.2.0
Jun 10, 2024
50b9c88
Next dev version
Jun 10, 2024
599b6a8
close #794
baudelotphilippe Jun 11, 2024
d76cf09
close #723
baudelotphilippe Jun 11, 2024
4577af0
close #733
baudelotphilippe Jun 11, 2024
73e8149
fix: support starting "/" for contexts
ptitFicus Jun 11, 2024
b3af4dc
remove ugly scrollbar
baudelotphilippe Jun 13, 2024
c6a8702
scroll without useless scrollbar
baudelotphilippe Jun 13, 2024
7c48752
fix#769 : Rebased to main
helakaraa Jun 17, 2024
2ad9b9d
fix#769 : modify scripts number
helakaraa Jun 17, 2024
dc6d23d
fix: global subcontext delete
ptitFicus Jun 14, 2024
867b345
chore: regression test for local context delete
ptitFicus Jun 14, 2024
58dd75d
fix: feature creation
ptitFicus Jun 14, 2024
b75c1ba
Update dev documentation
Jun 14, 2024
dc0beaf
test: webhooks
ptitFicus Jun 15, 2024
3c55ba3
feat: configurable exponential backoff for webhooks retry
ptitFicus Jun 15, 2024
4515f0f
Update dev documentation
Jun 15, 2024
1687894
chore: tests & doc for webhooks
ptitFicus Jun 16, 2024
3f50cd2
Update dev documentation
Jun 16, 2024
bea9fe9
doc: webhooks
ptitFicus Jun 16, 2024
637b3b1
Update dev documentation
Jun 16, 2024
9c66cde
doc: update screenshots
ptitFicus Jun 16, 2024
34f6e49
Update dev documentation
Jun 16, 2024
a35615e
feat: call webhook when feature name is updated
ptitFicus Jun 16, 2024
f6fa363
doc: complete & improve webhook doc
ptitFicus Jun 16, 2024
f38149a
Update dev documentation
Jun 16, 2024
4187077
chore: relax id props on some components
ptitFicus Jun 17, 2024
29e4152
chore: frontend deps update
ptitFicus Jun 17, 2024
e0a97f2
chore: fix some types
ptitFicus Jun 17, 2024
194d3b1
Setting version to 2.3.0
Jun 17, 2024
b988bb9
Next dev version
Jun 17, 2024
0038e72
fix#769 : reabase branch
helakaraa Jun 3, 2024
8ac72d9
fix#769 :add script view search for tenants
helakaraa Jun 4, 2024
60bdbda
fix#769 : rebase branch
helakaraa Jun 12, 2024
57793e3
fix#769 :change scripts number
helakaraa Jun 17, 2024
78e97c3
fix#769 : add project field to search view
helakaraa Jun 18, 2024
0e4381f
fix#769 : responsive button search + highlight selected searched item
helakaraa Jun 19, 2024
1af4397
fix#769 : add description as searchable item and add two switch butto…
helakaraa Jun 21, 2024
5552562
fix#769 : modify searchEntities implementation
helakaraa Jun 24, 2024
f06a257
fix#769 : add SearchAPISpec
helakaraa Jun 25, 2024
0a39044
fix#769 : filter GenericTable with the selected search value and filt…
helakaraa Jun 25, 2024
75af9b1
fix#769 : add apiKeys_project to search entities view
helakaraa Jun 25, 2024
1a4d7bd
feat: improve OIDC
ptitFicus Jun 10, 2024
4c36207
feat: webhooks
ptitFicus Apr 19, 2024
d085526
doc: fix izanami version in docker-compose
ptitFicus Jun 17, 2024
3006f87
Update dev documentation
Jun 17, 2024
1006b1e
feat: avoid calling stats endpoint on each connection
ptitFicus Jun 17, 2024
4730bea
fix typos
baudelotphilippe Jun 20, 2024
b91f7c2
add margin
baudelotphilippe Jun 20, 2024
fb819ff
fix typos
baudelotphilippe Jun 20, 2024
3e48f05
margin
baudelotphilippe Jun 20, 2024
705d770
improve display
baudelotphilippe Jun 20, 2024
0a94748
fix: type for user type
ptitFicus Jun 21, 2024
9763662
feat: allow space in feature names
ptitFicus Jun 21, 2024
c9b52ad
fix: overload wasm script read
ptitFicus Jun 21, 2024
d7f6270
feat: loading indicator for tenant creation form
ptitFicus Jun 22, 2024
112beaf
feat: add required field information on some forms
ptitFicus Jun 22, 2024
72c73b0
feat: better forms
ptitFicus Jun 23, 2024
f65a92b
feat: error message on modern feature form percentage field
ptitFicus Jun 23, 2024
9a6cc3f
fix: existing wasm script feature update
ptitFicus Jun 23, 2024
2b0357b
add margin between tenants btns
baudelotphilippe Jun 24, 2024
56de8e8
close #839
baudelotphilippe Jun 24, 2024
8ea92ce
feat: better forms
ptitFicus Jun 24, 2024
9e20c81
feat: limit tooltip count on body transformer
ptitFicus Jun 24, 2024
1441e88
fix: clear project selector on tenant change using sidebar
ptitFicus Jun 24, 2024
a5da47e
fix: keep tenant when refreshing global user / setting pages
ptitFicus Jun 24, 2024
0afdb17
feat: loader & mandatory fields on forms, when missing
ptitFicus Jun 24, 2024
e82aeaa
fix: frontend build
ptitFicus Jun 25, 2024
9de89c2
fix#769 : rebase to main
helakaraa Jun 3, 2024
5eb19fa
fix#769 :add script view search for tenants
helakaraa Jun 4, 2024
a288c18
fix#769 : modify search field to k command search button
helakaraa Jun 12, 2024
b4e509d
fix#769 : Rebased to main
helakaraa Jun 17, 2024
c706f8c
fix#769 : reabase branch
helakaraa Jun 3, 2024
23dd439
fix#769 :add script view search for tenants
helakaraa Jun 4, 2024
bf99481
fix#769 : rebase branch
helakaraa Jun 12, 2024
f515d7f
fix#769 :change scripts number
helakaraa Jun 17, 2024
5314bd8
fix#769 : add project field to search view
helakaraa Jun 18, 2024
06a30b3
fix#769 : responsive button search + highlight selected searched item
helakaraa Jun 19, 2024
5500994
fix#769 : modify searchEntities implementation
helakaraa Jun 24, 2024
cca8f44
fix#769 : filter GenericTable with the selected search value and filt…
helakaraa Jun 25, 2024
89e791a
fix#769 : table error tags
helakaraa Jun 26, 2024
3710022
feat: webhooks
ptitFicus Apr 19, 2024
b4b723d
fix: keep tenant when refreshing global user / setting pages
ptitFicus Jun 24, 2024
2e966f2
fix#769 : Omnisearch of entities only for default tenant
helakaraa Jun 3, 2024
56dd071
fix#769 :add script view search for tenants
helakaraa Jun 4, 2024
6df9c37
fix#769 : modify search field to k command search button
helakaraa Jun 12, 2024
de80f99
fix#769 : Rebased to main
helakaraa Jun 17, 2024
46f093e
fix#769 : reabase branch
helakaraa Jun 3, 2024
8818876
fix#769 :add script view search for tenants
helakaraa Jun 4, 2024
1587625
fix#769 : rebase branch
helakaraa Jun 12, 2024
bd4415d
fix#769 :change scripts number
helakaraa Jun 17, 2024
7e22858
fix#769 : add project field to search view
helakaraa Jun 18, 2024
264264b
fix#769 : responsive button search + highlight selected searched item
helakaraa Jun 19, 2024
419198e
fix#769 : add description as searchable item and add two switch butto…
helakaraa Jun 21, 2024
1157c83
fix#769 : filter GenericTable with the selected search value and filt…
helakaraa Jun 25, 2024
068f0f7
fix#769 : Rebased to main
helakaraa Jun 17, 2024
67eac90
fix#769 : resolve conflict
helakaraa Jun 26, 2024
91b6191
fix#769 : delete user from search modal
helakaraa Jun 26, 2024
e12e60f
fix#769 : fix filter project
helakaraa Jun 26, 2024
0f11181
fix#769 : add webhooks search view and filter
helakaraa Jun 27, 2024
f5792a0
fix#769 : modify ts_vector configuration to english
helakaraa Jun 28, 2024
89f26d7
fix#769 : frontEnd review
helakaraa Jul 2, 2024
6c52931
fix#769 : add clear button to input search field
helakaraa Jul 3, 2024
4ae0835
fix#769 : backend query search modification
helakaraa Jul 4, 2024
397504b
fix#769 : check admin role in search query
helakaraa Jul 5, 2024
bf71df1
fix#769 : add global and projects context , use Similarity method of …
helakaraa Jul 8, 2024
09ca13a
fix#769 : use Similarity method of fuzzystrmach on name and descript…
helakaraa Jul 10, 2024
ff8439a
fix: create extensions in flyway script
ptitFicus Jul 11, 2024
c15c0c0
fix: extension schema
ptitFicus Jul 11, 2024
0a17230
fix#769 : limit search to 10 results and add similarity fields in or…
helakaraa Jul 11, 2024
390e1a5
fix#769 : code review + check if user has tenants admin rights
helakaraa Jul 16, 2024
3d88771
fix#769 : add phonetic search method for name and description
helakaraa Jul 17, 2024
eebf1dc
fix#841 : Add wasm scripts to search display , team reviews
helakaraa Jul 23, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions app/fr/maif/izanami/application.scala
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ class IzanamiComponentsInstances(
lazy val eventController = wire[EventController]
lazy val webhookController = wire[WebhookController]
lazy val frontendController = wire[FrontendController]
lazy val searchController = wire[SearchController]

override lazy val assets: Assets = wire[Assets]
lazy val router: Router = {
Expand Down
184 changes: 184 additions & 0 deletions app/fr/maif/izanami/datastores/SearchDatastore.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
package fr.maif.izanami.datastores

import fr.maif.izanami.datastores.searchEntityImplicits.SearchEntityRow
import fr.maif.izanami.env.Env
import fr.maif.izanami.env.pgimplicits.EnhancedRow
import fr.maif.izanami.errors.IzanamiError
import fr.maif.izanami.models.{RightLevels, SearchEntity, TenantRight, User}
import fr.maif.izanami.utils.Datastore
import io.vertx.sqlclient.Row

import scala.concurrent.Future

class SearchDatastore(val env: Env) extends Datastore {
def searchEntitiesForAdmin(
tenants: List[String],
query: String
): Future[Either[IzanamiError, List[SearchEntity]]] = {
val searchQuery = tenants.zipWithIndex
.map { case (tenant, index) =>
val tenantPlaceholder = s"$$${index * 1 + 2}"
s"""
|SELECT
| origin_table,
| id,
| name,
| $tenantPlaceholder::TEXT AS origin_tenant,
| project,
| description,
| parent,
| izanami.SIMILARITY(name, $$1) as similarity_name,
| izanami.SIMILARITY(description, $$1) as similarity_description,
| GREATEST(
| izanami.SIMILARITY(name, $$1),
| izanami.SIMILARITY(description, $$1)
| ) AS match_score
|FROM $tenant.search_entities
|WHERE izanami.SIMILARITY(name, $$1) > 0.1
| OR izanami.SIMILARITY(description, $$1) > 0.1
| OR izanami.SOUNDEX(name) = izanami.SOUNDEX($$1)
| OR izanami.SOUNDEX(description) = izanami.SOUNDEX($$1)
""".stripMargin
}
.mkString(" UNION ALL ") + "ORDER BY match_score DESC LIMIT 10"

val params = List.newBuilder[AnyRef]
params += query
tenants.foreach { tenant =>
params += tenant
}
env.postgresql
.queryAll(searchQuery, params.result()) { r => r.optSearchEntity() }
.map { entities => Right(entities) }
.recover { case ex =>
logger.error("Error while searching entities", ex)
Right(List.empty[SearchEntity])
}

}

def searchEntities(
tenantRights: Map[String, TenantRight],
query: String
): Future[Either[IzanamiError, List[SearchEntity]]] = {


val adminQuery = (index: Int, tenant: String) =>
s"""
SELECT
origin_table,
id,
name,
$$${index + 1}::TEXT AS origin_tenant,
project,
description,
parent,
izanami.SIMILARITY(name, $$${index}) as similarity_name,
izanami.SIMILARITY(description, $$${index}) as similarity_description,
GREATEST(
izanami.SIMILARITY(name, $$${index}),
izanami.SIMILARITY(description, $$${index})
) AS match_score
FROM $tenant.search_entities
WHERE izanami.SIMILARITY(name, $$${index}) > 0.4
OR izanami.SIMILARITY(description, $$${index}) > 0.4
OR izanami.SOUNDEX(name) = izanami.SOUNDEX($$${index})
OR izanami.SOUNDEX(description) = izanami.SOUNDEX($$${index})
"""

val nonAdminQuery = (index: Int, tenant: String) =>
s"""
SELECT
origin_table,
id,
name,
$$${index + 1}::TEXT AS origin_tenant,
project,
description,
parent,
izanami.SIMILARITY(name, $$${index}) as similarity_name,
izanami.SIMILARITY(description, $$${index}) as similarity_description,
GREATEST(
izanami.SIMILARITY(name, $$${index}),
izanami.SIMILARITY(description, $$${index})
) AS match_score
FROM $tenant.search_entities
WHERE
((project = ANY ($$${index + 2}::TEXT[]) AND origin_table IN ($$${index + 3}, $$${index + 4}))
OR (origin_table=$$${index + 5} AND name = ANY ($$${index + 6}::TEXT[]))
OR (origin_table=$$${index + 7} AND name = ANY ($$${index + 8}::TEXT[]))
OR (origin_table=$$${index + 9} AND project IS NULL))
AND izanami.SIMILARITY(name, $$${index}) > 0.4
OR izanami.SIMILARITY(description, $$${index}) > 0.4
OR izanami.SOUNDEX(name) = izanami.SOUNDEX($$${index})
OR izanami.SOUNDEX(description) = izanami.SOUNDEX($$${index})
"""

var currentIndex = 1

val searchQuery = tenantRights.zipWithIndex.map { case ((tenant, right), _) =>
val queryPart = if (right.level == RightLevels.Admin) {
val q = adminQuery(currentIndex, tenant)
currentIndex += 2
q
} else {
val q = nonAdminQuery(currentIndex, tenant)
currentIndex += 10
q
}
queryPart
}.mkString(" UNION ALL ") + " ORDER BY match_score DESC LIMIT 10"

val params = List.newBuilder[AnyRef]
tenantRights.foreach { case (tenant, tenantRight) =>
params += query
params += tenant
if (tenantRight.level != RightLevels.Admin) {
params += tenantRight.projects.keys.toArray
params += "Projects"
params += "Features"
params += "Webhooks"
params += tenantRight.webhooks.keys.toArray
params += "Apikeys"
params += tenantRight.keys.keys.toArray
params += "Tags"
}
}

env.postgresql
.queryAll(searchQuery, params.result()) { r => r.optSearchEntity() }
.map { entities => Right(entities) }
.recover { case ex =>
logger.error("Error while searching entities", ex)
Right(List.empty[SearchEntity])
}
}
}
object searchEntityImplicits {
implicit class SearchEntityRow(val row: Row) extends AnyVal {
def optSearchEntity(): Option[SearchEntity] = {
for (
name <- row.optString("name");
origin_table <- row.optString("origin_table");
origin_tenant <- row.optString("origin_tenant");
id <- row.optString("id");
project <- Some(row.optString("project"));
description <- Some(row.optString("description"));
parent <- Some(row.optString("parent"));
similarity_name <- Some(row.optDouble("similarity_name"));
similarity_description <- Some(row.optDouble("similarity_description"))
)
yield SearchEntity(
id = id,
name = name,
origin_table = origin_table,
origin_tenant = origin_tenant,
project = project,
description = description,
parent = parent,
similarity_name = similarity_name,
similarity_description = similarity_description
)
}
}
}
1 change: 1 addition & 0 deletions app/fr/maif/izanami/env/env.scala
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ class Datastores(env: Env) {
val configuration: ConfigurationDatastore = new ConfigurationDatastore(env)
val webhook: WebhooksDatastore = new WebhooksDatastore(env)
val stats: StatsDatastore = new StatsDatastore(env)
val searchQueries : SearchDatastore = new SearchDatastore(env)

def onStart(): Future[Unit] = {
for {
Expand Down
32 changes: 32 additions & 0 deletions app/fr/maif/izanami/models/SearchEntity.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package fr.maif.izanami.models

import play.api.libs.json.{Json, Writes}

case class SearchEntity(
id: String,
name: String,
origin_table: String,
origin_tenant: String,
project: Option[String],
description: Option[String],
parent: Option[String],
similarity_name: Option[Double],
similarity_description: Option[Double],
)

object SearchEntity {
implicit val searchEntityWrites: Writes[SearchEntity] = { searchEntity =>
Json.obj(
"id" -> searchEntity.id,
"name" -> searchEntity.name,
"origin_table" -> searchEntity.origin_table,
"origin_tenant" -> searchEntity.origin_tenant,
"project" -> searchEntity.project,
"description" -> searchEntity.description,
"parent" -> searchEntity.parent,
"similarity_name" -> searchEntity.similarity_name,
"similarity_description" -> searchEntity.similarity_description
)
}

}
78 changes: 78 additions & 0 deletions app/fr/maif/izanami/web/SearchController.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package fr.maif.izanami.web

import fr.maif.izanami.env.Env
import play.api.libs.json.Json
import play.api.mvc._

import scala.concurrent.{ExecutionContext, Future}

class SearchController(
val env: Env,
val controllerComponents: ControllerComponents,
val tenantAuthAction: TenantAuthActionFactory,
val userDetailedAuthAction: DetailledAuthAction
) extends BaseController {
implicit val ec: ExecutionContext = env.executionContext

def searchEntities(query: String): Action[AnyContent] = userDetailedAuthAction.async {
implicit request: UserRequestWithCompleteRights[AnyContent] =>
val userTenants = request.user.rights.tenants

if (request.user.admin) {
env.datastores.tenants
.readTenants()
.flatMap(tenants =>
env.datastores.searchQueries
.searchEntitiesForAdmin(tenants.map(tenant => tenant.name), query)
.map(entities =>
entities.fold(
err => Results.Status(err.status)(Json.toJson(err)),
entities => Ok(Json.toJson(entities))
)
)
)

} else if (userTenants.nonEmpty) {
env.datastores.searchQueries
.searchEntities(userTenants, query)
.map(entities =>
entities.fold(
err => Results.Status(err.status)(Json.toJson(err)),
entities => Ok(Json.toJson(entities))
)
)
} else {
Future.successful(Forbidden(Json.obj("message" -> "User has no tenants rights ")))
}

}

def searchEntitiesByTenant(tenant: String, query: String): Action[AnyContent] = userDetailedAuthAction.async {
implicit request: UserRequestWithCompleteRights[AnyContent] =>
val userTenants = request.user.rights.tenants

if (request.user.admin) {
env.datastores.searchQueries
.searchEntitiesForAdmin(List(tenant), query)
.map(entities =>
entities.fold(
err => Results.Status(err.status)(Json.toJson(err)),
entities => Ok(Json.toJson(entities))
)
)
} else if (userTenants.nonEmpty && userTenants.contains(tenant)) {
val filteredTenants = userTenants.filter { case (t, _) => t == tenant }
env.datastores.searchQueries
.searchEntities(filteredTenants, query)
.map(entities =>
entities.fold(
err => Results.Status(err.status)(Json.toJson(err)),
entities => Ok(Json.toJson(entities))
)
)
} else {
Future.successful(Forbidden(Json.obj("message" -> s"User has no rights for this tenant ${tenant}")))
}
}

}
4 changes: 4 additions & 0 deletions conf/routes
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ GET /api/admin/tenants/:tenant/webhooks/:id/users
PUT /api/admin/tenants/:tenant/webhooks/:webhook/users/:user/rights fr.maif.izanami.web.UserController.updateUserRightsForWebhook(tenant: String, webhook: String, user: String)


# Search application endpoints
GET /api/admin/search fr.maif.izanami.web.SearchController.searchEntities(query: String)
GET /api/admin/tenants/:tenant/search fr.maif.izanami.web.SearchController.searchEntitiesByTenant(tenant: String, query: String)

# Client application endpoints
GET /api/v2/features/:id fr.maif.izanami.web.FeatureController.checkFeatureForContext(id: String, user: String ?= "", context: fr.maif.izanami.web.FeatureContextPath)
POST /api/v2/features/:id fr.maif.izanami.web.FeatureController.checkFeatureForContext(id: String, user: String ?= "", context: fr.maif.izanami.web.FeatureContextPath)
Expand Down
Loading