Skip to content

Commit

Permalink
fix: document updates to work in cases user role is changed (#73)
Browse files Browse the repository at this point in the history
  • Loading branch information
jachro authored Mar 26, 2024
1 parent b77fc64 commit ce526d6
Show file tree
Hide file tree
Showing 10 changed files with 154 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ final class MessageHandlers[F[_]: Async](
val projectCreated: Stream[F, Unit] =
add(cfg.projectCreated, makeUpsert[ProjectCreated](cfg.projectCreated))

val projectUpdated =
val projectUpdated: Stream[F, Unit] =
add(
cfg.projectUpdated,
makeUpdated[ProjectUpdated](cfg.projectUpdated, DocumentUpdates.project)
Expand Down Expand Up @@ -75,10 +75,10 @@ final class MessageHandlers[F[_]: Async](
val userAdded: Stream[F, Unit] =
add(cfg.userAdded, makeCreated[UserAdded](cfg.userAdded))

val userUpdated =
val userUpdated: Stream[F, Unit] =
add(cfg.userUpdated, makeUpdated[UserUpdated](cfg.userUpdated, DocumentUpdates.user))

val userRemoved =
val userRemoved: Stream[F, Unit] =
val ps = steps(cfg.userRemoved)
add(
cfg.userRemoved,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ package io.renku.search.provision.handler
import io.github.arainko.ducktape.*
import io.renku.events.v1.*
import io.renku.search.model.Id
import io.renku.search.provision.TypeTransformers.given
import io.renku.search.provision.handler.TypeTransformers.given
import io.renku.search.solr.documents.EntityDocument
import io.renku.search.solr.documents.Project as ProjectDocument
import io.renku.search.solr.documents.User as UserDocument
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import cats.syntax.all.*
import io.github.arainko.ducktape.*
import io.renku.events.v1.*
import io.renku.search.model.Id
import io.renku.search.provision.TypeTransformers.given
import io.renku.search.provision.handler.TypeTransformers.given
import io.renku.search.solr.documents.EntityDocument
import io.renku.search.solr.documents.PartialEntityDocument
import io.renku.search.solr.documents.Project as ProjectDocument
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import cats.syntax.all.*
import io.github.arainko.ducktape.*
import io.renku.events.v1.*
import io.renku.search.model.Id
import io.renku.search.provision.TypeTransformers.given
import io.renku.search.provision.handler.TypeTransformers.given
import io.renku.search.solr.documents.EntityDocument
import io.renku.search.solr.documents.Project as ProjectDocument
import io.renku.search.solr.documents.User as UserDocument
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* limitations under the License.
*/

package io.renku.search.provision
package io.renku.search.provision.handler

import io.github.arainko.ducktape.*
import io.renku.events.v1
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import io.github.arainko.ducktape.*
import io.renku.events.v1.ProjectCreated
import io.renku.events.v1.ProjectUpdated
import io.renku.search.model.Id
import io.renku.search.provision.TypeTransformers.given
import io.renku.search.provision.handler.TypeTransformers.given
import io.renku.search.solr.documents.Project

trait ProjectSyntax:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@

package io.renku.search.solr.documents

import io.bullet.borer.NullOptions.given
import io.bullet.borer.*
import io.bullet.borer.NullOptions.given
import io.bullet.borer.derivation.MapBasedCodecs
import io.renku.search.model.*
import io.renku.search.model.projects.MemberRole
import io.renku.search.model.projects.MemberRole.{Member, Owner}
import io.renku.search.model.projects.Visibility
import io.renku.search.model.projects.{MemberRole, Visibility}
import io.renku.search.solr.schema.EntityDocumentSchema.Fields
import io.renku.solr.client.EncoderSupport
import io.renku.solr.client.EncoderSupport.*

sealed trait EntityDocument extends SolrDocument:
val score: Option[Double]
Expand Down Expand Up @@ -55,8 +55,18 @@ final case class Project(
) extends EntityDocument:
def addMember(userId: Id, role: MemberRole): Project =
role match {
case Owner => copy(owners = (userId :: owners).distinct, score = None)
case Member => copy(members = (userId :: members).distinct, score = None)
case Owner =>
copy(
owners = (userId :: owners).distinct,
members = members.filterNot(_ == userId).distinct,
score = None
)
case Member =>
copy(
owners = owners.filterNot(_ == userId).distinct,
members = (userId :: members).distinct,
score = None
)
}

def addMembers(role: MemberRole, ids: List[Id]): Project = role match
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,8 +48,8 @@ object PartialEntityDocument:
def remove(id: Id): Project = copy(owners = owners - id, members = members - id)
def add(id: Id, role: MemberRole): Project =
role match
case MemberRole.Owner => copy(owners = owners + id)
case MemberRole.Member => copy(members = members + id)
case MemberRole.Owner => copy(owners = owners + id, members = members - id)
case MemberRole.Member => copy(members = members + id, owners = owners - id)

def applyTo(e: EntityDocument): EntityDocument =
e match
Expand All @@ -61,8 +61,8 @@ object PartialEntityDocument:
def combine(p: Project): Project =
if (p.id == id)
p.copy(
members = p.members ++ members,
owners = p.owners ++ owners
members = p.members ++ (members -- p.owners),
owners = p.owners ++ (owners -- p.members)
)
else p

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright 2024 Swiss Data Science Center (SDSC)
* A partnership between École Polytechnique Fédérale de Lausanne (EPFL) and
* Eidgenössische Technische Hochschule Zürich (ETHZ).
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package io.renku.search.solr.documents

import io.renku.search.GeneratorSyntax
import io.renku.search.model.ModelGenerators.{idGen, projectMemberRoleGen}
import io.renku.search.model.projects
import io.renku.search.model.projects.MemberRole.{Member, Owner}
import io.renku.search.solr.client.SolrDocumentGenerators.partialProjectGen
import io.renku.search.solr.documents.PartialEntityDocument.Project
import munit.{FunSuite, ScalaCheckSuite}
import org.scalacheck.Prop

class PartialProjectSpec extends ScalaCheckSuite with GeneratorSyntax:

test("add should add the userId to the relevant bucket"):
Prop.forAll(partialProjectGen, idGen, projectMemberRoleGen) {
case (existing, userId, role) =>
val updated = existing.add(userId, role)

role match {
case Owner =>
assertEquals(updated.owners, existing.owners + userId)
assertEquals(updated.members, existing.members)
case Member =>
assertEquals(updated.members, existing.members + userId)
assertEquals(updated.owners, existing.owners)
}
}

test(
"add should add the userId to the relevant bucket and remove from the other bucket if existed"
):
Prop.forAll(idGen, idGen, projectMemberRoleGen) { case (projectId, userId, role) =>
val existing = Project(projectId, Set(userId), Set(userId))

val updated = existing.add(userId, role)

role match {
case Owner =>
assertEquals(updated.owners, existing.owners)
assertEquals(updated.members, existing.members - userId)
case Member =>
assertEquals(updated.members, existing.members)
assertEquals(updated.owners, existing.owners - userId)
}
}

test("applyTo should add members and owners from the caller object"):
Prop.forAll(partialProjectGen, idGen, projectMemberRoleGen) {
case (existing, userId, role) =>
val update = Project(existing.id).add(userId, role)

val updated = existing.applyTo(update).asInstanceOf[Project]

role match {
case Owner =>
assertEquals(updated.owners, existing.owners + userId)
assertEquals(updated.members, existing.members)
case Member =>
assertEquals(updated.members, existing.members + userId)
assertEquals(updated.owners, existing.owners)
}
}

test(
"applyTo should add members and owners from the caller object except those that have been moved between buckets"
):
Prop.forAll(idGen, idGen, projectMemberRoleGen) { case (projectId, userId, role) =>
val existing = Project(projectId, Set(userId), Set(userId))

val update = Project(projectId).add(userId, role)

val updated = existing.applyTo(update).asInstanceOf[Project]

role match {
case Owner =>
assertEquals(updated.owners, existing.owners)
assertEquals(updated.members, existing.members - userId)
case Member =>
assertEquals(updated.members, existing.members)
assertEquals(updated.owners, existing.owners - userId)
}
}

test("removeMember should remove the given userId from the correct bucket in the doc"):
Prop.forAll(partialProjectGen, idGen, projectMemberRoleGen) {
case (project, userId, role) =>
val updated = project.add(userId, role)
assertEquals(updated.remove(userId), project)
}

test("removeMember should do nothing if there's no member/owner with the given userId"):
Prop.forAll(partialProjectGen, idGen, idGen, projectMemberRoleGen) {
case (project, existingUserId, toDeleteUserId, role) =>
val updated = project.add(existingUserId, role)
assertEquals(updated.remove(toDeleteUserId), updated)
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
package io.renku.search.solr.documents

import io.renku.search.model.ModelGenerators.{idGen, projectMemberRoleGen}
import io.renku.search.model.projects
import io.renku.search.model.projects.MemberRole.{Member, Owner}
import io.renku.search.solr.client.SolrDocumentGenerators.projectDocumentGen
import munit.{FunSuite, ScalaCheckSuite}
Expand All @@ -38,6 +39,18 @@ class ProjectSpec extends ScalaCheckSuite:
}
}

test(
"addMember should add the given userId to the correct bucket and remove from the other buckets"
):
Prop.forAll(projectDocumentGen, idGen) { case (project, userId) =>
val updated = project
.addMember(userId, projects.MemberRole.Member)
.addMember(userId, projects.MemberRole.Owner)

assertEquals(updated.owners, (userId :: project.owners).distinct)
assertEquals(updated.members, project.members.filterNot(_ == userId).distinct)
}

test("addMember should not add duplicates"):
Prop.forAll(projectDocumentGen, idGen, projectMemberRoleGen) {
case (project, userId, role) =>
Expand Down

0 comments on commit ce526d6

Please sign in to comment.