Skip to content

Commit

Permalink
Add stickyNotes length and count validation, add stickyNotes configur…
Browse files Browse the repository at this point in the history
…ation, fix error method (or did i?)
  • Loading branch information
philemone committed Nov 19, 2024
1 parent cdf599c commit 4bf3249
Show file tree
Hide file tree
Showing 11 changed files with 131 additions and 16 deletions.
5 changes: 3 additions & 2 deletions designer/client/src/actions/notificationActions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,11 @@ export function success(message: string): Action {
});
}

export function error(message: string): Action {
//TODO please take a look at this method and my changes, am I wrong or was it incomplete (without `error` and `showErrorText`) and had incomplete logic
export function error(message: string, error?: string, showErrorText?: boolean): Action {
return Notifications.error({
autoDismiss: 10,
children: <Notification type={"error"} icon={<InfoOutlinedIcon />} message={message} />,
children: <Notification type={"error"} icon={<InfoOutlinedIcon />} message={showErrorText ? error : message} />,
});
}

Expand Down
1 change: 0 additions & 1 deletion designer/client/src/components/StickyNotePreview.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ export function StickyNotePreview({ node, isActive, isOver }: { node: NodeType;
});

const imageColors = css({
background: theme.palette.custom.getNodeStyles(node)?.fill,
color: theme.palette.common.black,
});

Expand Down
6 changes: 5 additions & 1 deletion designer/client/src/components/graph/EspNode/stickyNote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,11 @@ const body: dia.MarkupNodeJSON = {

const renderer = new marked.Renderer();
renderer.link = function (href, title, text) {
return `<a target="_blank" href="${href}">${text}` + "</a>";
return `<a target="_blank" href="${href}">${text}</a>`;
};
renderer.image = function (href, title, text) {
// SVG don't support HTML img inside foreignObject
return `<a target="_blank" href="${href}">${text} (attached img)</a>`;
};

const foreignObject = (stickyNote: StickyNote): MarkupNodeJSON => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function StickyNotesPanel(props: ToolbarPanelProps): JSX.Element {
<Tool
nodeModel={noteModel}
label={t("stickyNotes.tool.label", "sticky note")}
key={StickyNoteType}
key={StickyNoteType + "_" + pristine}
disabled={!pristine}
/>
</StyledToolbox>
Expand Down
5 changes: 5 additions & 0 deletions designer/server/src/main/resources/defaultDesignerConfig.conf
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,11 @@ testDataSettings: {
resultsMaxBytes: 50000000
}

stickyNotesSettings: {
maxContentLength: 5000,
maxNotesCount: 5
}

scenarioLabelSettings: {
validationRules = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,15 @@ import pl.touk.nussknacker.ui.api.description.stickynotes.Dtos.{
StickyNoteCorrelationId,
StickyNoteId,
StickyNoteUpdateRequest,
StickyNotesError
StickyNotesError,
StickyNotesSettings
}
import pl.touk.nussknacker.ui.api.description.stickynotes.Dtos.StickyNotesError.{
NoPermission,
NoScenario,
StickyNoteContentTooLong,
StickyNoteCountLimitReached
}
import pl.touk.nussknacker.ui.api.description.stickynotes.Dtos.StickyNotesError.{NoPermission, NoScenario}
import pl.touk.nussknacker.ui.process.repository.stickynotes.StickyNotesRepository
import pl.touk.nussknacker.ui.process.repository.DBIOActionRunner
import pl.touk.nussknacker.ui.process.ProcessService
Expand All @@ -28,6 +34,7 @@ class StickyNotesApiHttpService(
scenarioService: ProcessService,
scenarioAuthorizer: AuthorizeProcess,
dbioActionRunner: DBIOActionRunner,
stickyNotesSettings: StickyNotesSettings
)(implicit executionContext: ExecutionContext)
extends BaseHttpService(authManager)
with LazyLogging {
Expand Down Expand Up @@ -58,6 +65,9 @@ class StickyNotesApiHttpService(
for {
scenarioId <- getScenarioIdByName(scenarioName)
_ <- isAuthorized(scenarioId, Permission.Write)
count <- getStickyNotesCount(scenarioId, requestBody.scenarioVersionId)
_ <- validateStickyNotesCount(count, stickyNotesSettings)
_ <- validateStickyNoteContent(requestBody.content, stickyNotesSettings)
processActivity <- addStickyNote(scenarioId, requestBody)
} yield processActivity
}
Expand All @@ -72,6 +82,7 @@ class StickyNotesApiHttpService(
for {
scenarioId <- getScenarioIdByName(scenarioName)
_ <- isAuthorized(scenarioId, Permission.Write)
_ <- validateStickyNoteContent(requestBody.content, stickyNotesSettings)
processActivity <- updateStickyNote(requestBody)
} yield processActivity.toInt
}
Expand Down Expand Up @@ -122,6 +133,16 @@ class StickyNotesApiHttpService(
)
)

private def getStickyNotesCount(scenarioId: ProcessId, versionId: VersionId): EitherT[Future, StickyNotesError, Int] =
EitherT
.right(
dbioActionRunner
.run(
stickyNotesRepository.findStickyNotes(scenarioId, versionId)
)
.map(_.length)
)

private def deleteStickyNote(noteId: StickyNoteId)(
implicit loggedUser: LoggedUser
): EitherT[Future, StickyNotesError, Int] =
Expand All @@ -140,6 +161,18 @@ class StickyNotesApiHttpService(
)
} yield result

private def validateStickyNotesCount(
stickyNotesCount: Int,
stickyNotesConfig: StickyNotesSettings
): EitherT[Future, StickyNotesError, Unit] =
EitherT.fromEither(
Either.cond(
stickyNotesCount < stickyNotesConfig.maxNotesCount,
(),
StickyNoteCountLimitReached(stickyNotesConfig.maxNotesCount)
)
)

private def addStickyNote(scenarioId: ProcessId, requestBody: StickyNoteAddRequest)(
implicit loggedUser: LoggedUser
): EitherT[Future, StickyNotesError, StickyNoteCorrelationId] =
Expand All @@ -158,6 +191,18 @@ class StickyNotesApiHttpService(
)
)

private def validateStickyNoteContent(
content: String,
stickyNotesConfig: StickyNotesSettings
): EitherT[Future, StickyNotesError, Unit] =
EitherT.fromEither(
Either.cond(
content.length <= stickyNotesConfig.maxContentLength,
(),
StickyNoteContentTooLong(content.length, stickyNotesConfig.maxContentLength)
)
)

private def updateStickyNote(requestBody: StickyNoteUpdateRequest)(
implicit loggedUser: LoggedUser
): EitherT[Future, StickyNotesError, Int] = for {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,20 @@ import pl.touk.nussknacker.restmodel.BaseEndpointDefinitions.SecuredEndpoint
import pl.touk.nussknacker.security.AuthCredentials
import pl.touk.nussknacker.ui.api.TapirCodecs
import pl.touk.nussknacker.ui.api.TapirCodecs.ScenarioNameCodec._
import pl.touk.nussknacker.ui.api.description.StickyNotesApiEndpoints.Examples.{noScenarioExample, noStickyNoteExample}
import pl.touk.nussknacker.ui.api.description.StickyNotesApiEndpoints.Examples.{
noScenarioExample,
noStickyNoteExample,
stickyNoteContentTooLongExample,
stickyNoteCountLimitReachedExample
}
import pl.touk.nussknacker.ui.api.description.stickynotes.Dtos.StickyNoteId
import pl.touk.nussknacker.ui.api.description.stickynotes.Dtos.StickyNotesError.{NoScenario, NoStickyNote}
import sttp.model.StatusCode.{NotFound, Ok}
import pl.touk.nussknacker.ui.api.description.stickynotes.Dtos.StickyNotesError.{
NoScenario,
NoStickyNote,
StickyNoteContentTooLong,
StickyNoteCountLimitReached
}
import sttp.model.StatusCode.{BadRequest, NotFound, Ok}
import sttp.tapir.EndpointIO.Example
import sttp.tapir._
import sttp.tapir.json.circe.jsonBody
Expand Down Expand Up @@ -102,7 +112,8 @@ class StickyNotesApiEndpoints(auth: EndpointInput[AuthCredentials]) extends Base
.errorOut(
oneOf[StickyNotesError](
noScenarioExample,
noStickyNoteExample
noStickyNoteExample,
stickyNoteContentTooLongExample
)
)
.withSecurity(auth)
Expand All @@ -122,7 +133,9 @@ class StickyNotesApiEndpoints(auth: EndpointInput[AuthCredentials]) extends Base
.out(jsonBody[StickyNoteCorrelationId])
.errorOut(
oneOf[StickyNotesError](
noScenarioExample
noScenarioExample,
stickyNoteContentTooLongExample,
stickyNoteCountLimitReachedExample
)
)
.withSecurity(auth)
Expand Down Expand Up @@ -176,6 +189,32 @@ object StickyNotesApiEndpoints {
)
)

val stickyNoteContentTooLongExample: EndpointOutput.OneOfVariant[StickyNoteContentTooLong] =
oneOfVariantFromMatchType(
BadRequest,
plainBody[StickyNoteContentTooLong]
.example(
Example.of(
summary = Some("Provided note content is too long (5004 characters). Max content length is 5000."),
value = StickyNoteContentTooLong(count = 5004, max = 5000)
)
)
)

val stickyNoteCountLimitReachedExample: EndpointOutput.OneOfVariant[StickyNoteCountLimitReached] =
oneOfVariantFromMatchType(
BadRequest,
plainBody[StickyNoteCountLimitReached]
.example(
Example.of(
summary = Some(
"Cannot add another sticky note, since max number of sticky notes was reached: 5 (see configuration)."
),
value = StickyNoteCountLimitReached(max = 5)
)
)
)

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ object Dtos {
targetEdge: Option[String]
)

// TODO add this to configuration file in next iteration
case class StickyNotesSettings(
maxContentLength: Int,
maxNotesCount: Int
)

sealed trait StickyNotesError

implicit lazy val cellErrorSchema: Schema[LayoutData] = Schema.derived
Expand All @@ -82,6 +88,7 @@ object Dtos {
final case class NoScenario(scenarioName: ProcessName) extends StickyNotesError
final case object NoPermission extends StickyNotesError with CustomAuthorizationError
final case class StickyNoteContentTooLong(count: Int, max: Int) extends StickyNotesError
final case class StickyNoteCountLimitReached(max: Int) extends StickyNotesError
final case class NoStickyNote(noteId: StickyNoteId) extends StickyNotesError

implicit val noScenarioCodec: Codec[String, NoScenario, CodecFormat.TextPlain] =
Expand All @@ -92,9 +99,14 @@ object Dtos {
s"No sticky note with id: ${e.noteId} was found"
)

implicit val noCommentCodec: Codec[String, StickyNoteContentTooLong, CodecFormat.TextPlain] =
implicit val stickyNoteContentTooLongCodec: Codec[String, StickyNoteContentTooLong, CodecFormat.TextPlain] =
BaseEndpointDefinitions.toTextPlainCodecSerializationOnly[StickyNoteContentTooLong](e =>
s"Provided note content is too long (${e.count} characters). Max content length is ${e.max} "
s"Provided note content is too long (${e.count} characters). Max content length is ${e.max}."
)

implicit val stickyNoteCountLimitReachedCodec: Codec[String, StickyNoteCountLimitReached, CodecFormat.TextPlain] =
BaseEndpointDefinitions.toTextPlainCodecSerializationOnly[StickyNoteCountLimitReached](e =>
s"Cannot add another sticky note, since max number of sticky notes was reached: ${e.max} (see configuration)."
)

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import net.ceedubs.ficus.readers.ValueReader
import pl.touk.nussknacker.engine.definition.component.Components.ComponentDefinitionExtractionMode
import pl.touk.nussknacker.engine.util.config.FicusReaders
import pl.touk.nussknacker.ui.api._
import pl.touk.nussknacker.ui.api.description.stickynotes.Dtos.StickyNotesSettings
import pl.touk.nussknacker.ui.config.Implicits.parseOptionalConfig
import pl.touk.nussknacker.ui.process.migrate.HttpRemoteEnvironmentConfig

Expand All @@ -28,7 +29,8 @@ final case class FeatureTogglesConfig(
testDataSettings: TestDataSettings,
enableConfigEndpoint: Boolean,
redirectAfterArchive: Boolean,
componentDefinitionExtractionMode: ComponentDefinitionExtractionMode
componentDefinitionExtractionMode: ComponentDefinitionExtractionMode,
stickyNotesSettings: StickyNotesSettings
)

object FeatureTogglesConfig extends LazyLogging {
Expand Down Expand Up @@ -56,6 +58,7 @@ object FeatureTogglesConfig extends LazyLogging {
val tabs = parseOptionalConfig[List[TopTab]](config, "tabs")
val intervalTimeSettings = config.as[IntervalTimeSettings]("intervalTimeSettings")
val testDataSettings = config.as[TestDataSettings]("testDataSettings")
val stickyNotesSettings = config.as[StickyNotesSettings]("stickyNotesSettings")
val redirectAfterArchive = config.getAs[Boolean]("redirectAfterArchive").getOrElse(true)
val componentDefinitionExtractionMode = parseComponentDefinitionExtractionMode(config)

Expand All @@ -76,6 +79,7 @@ object FeatureTogglesConfig extends LazyLogging {
enableConfigEndpoint = enableConfigEndpoint,
redirectAfterArchive = redirectAfterArchive,
componentDefinitionExtractionMode = componentDefinitionExtractionMode,
stickyNotesSettings = stickyNotesSettings
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,8 @@ class AkkaHttpBasedRouteProvider(
stickyNotesRepository = stickyNotesRepository,
scenarioService = processService,
scenarioAuthorizer = processAuthorizer,
dbioRunner
dbioRunner,
stickyNotesSettings = featureTogglesConfig.stickyNotesSettings
)

val scenarioActivityApiHttpService = new ScenarioActivityApiHttpService(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ testDataSettings: {
resultsMaxBytes: 50000000
}

stickyNotesSettings: {
maxContentLength: 5000,
maxNotesCount: 5
}

scenarioLabelSettings: {
validationRules = [
{
Expand Down

0 comments on commit 4bf3249

Please sign in to comment.