Skip to content

Commit

Permalink
seems to work
Browse files Browse the repository at this point in the history
  • Loading branch information
tpolecat committed Nov 18, 2022
1 parent 10de2e2 commit 831e788
Show file tree
Hide file tree
Showing 17 changed files with 210 additions and 6 deletions.
4 changes: 2 additions & 2 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ val clueVersion = "0.23.1"
val declineVersion = "2.3.1"
val disciplineMunitVersion = "1.0.9"
val flywayVersion = "9.8.1"
val grackleVersion = "0.8.2"
val grackleVersion = "0.8.1-2-49a9931-20221117T153900Z-SNAPSHOT"
val http4sBlazeVersion = "0.23.12"
val http4sEmberVersion = "0.23.16"
val http4sJdkHttpClientVersion = "0.7.0"
val jwtVersion = "5.0.0"
val logbackVersion = "1.4.4"
val log4catsVersion = "2.5.0"
val lucumaCoreVersion = "0.57.0"
val lucumaGraphQLRoutesVersion = "0.5.5"
val lucumaGraphQLRoutesVersion = "0.5.5-13-018bb63-20221117T150008Z-SNAPSHOT"
val munitVersion = "0.7.29"
val munitCatsEffectVersion = "1.0.7"
val munitDisciplineVersion = "1.0.9"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-- https://stackoverflow.com/questions/54372666/create-an-immutable-clone-of-concat-ws/54384767#54384767
CREATE OR REPLACE FUNCTION immutable_concat_ws(text, VARIADIC text[])
RETURNS text AS 'text_concat_ws' LANGUAGE internal IMMUTABLE PARALLEL SAFE;
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,22 @@ create table t_observation (
c_hour_angle_min d_hour_angle null default null,
c_hour_angle_max d_hour_angle null default null,

-- a column we can use to identify and join with distinct observing conditions in this program
c_conditions_key text not null generated always as (
immutable_concat_ws(
',',
c_program_id,
c_cloud_extinction,
c_image_quality,
c_sky_background,
c_water_vapor,
coalesce(c_air_mass_min::text, 'null'),
coalesce(c_air_mass_max::text, 'null'),
coalesce(c_hour_angle_min::text, 'null'),
coalesce(c_hour_angle_max::text, 'null')
)
) stored,

-- observing conditions: both air mass fields are defined or neither are defined
constraint air_mass_neither_or_both
check (num_nulls(c_air_mass_min, c_air_mass_max) <> 1),
Expand Down Expand Up @@ -217,6 +233,8 @@ create table t_observation (
);
comment on table t_observation is 'Observations.';

create index on t_observation (c_conditions_key);

create view v_observation as
select *,
case when c_explicit_ra is not null then c_observation_id end as c_explicit_base_id,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
CREATE view v_constraint_set_group AS
SELECT
DISTINCT c_conditions_key,
c_program_id,
max(c_observation_id) as c_observation_id -- arbitrary, just pick one
FROM
t_observation
GROUP BY
c_conditions_key,
c_program_id;

Original file line number Diff line number Diff line change
Expand Up @@ -4695,6 +4695,9 @@ type Query {

# Selects the first `LIMIT` matching observations based on the provided `WHERE` parameter, if any.
observations(

programId: ProgramId

# Filters the selection of observations.
WHERE: WhereObservation

Expand Down
3 changes: 1 addition & 2 deletions modules/service/src/main/scala/lucuma/odb/Main.scala
Original file line number Diff line number Diff line change
Expand Up @@ -116,10 +116,9 @@ object Main extends IOApp {
for {
pool <- databasePoolResource[F](databaseConfig)
ssoClient <- ssoClientResource
// channels <- OdbMapping.Channels(pool)
userSvc <- pool.map(UserService.fromSession(_))
middleware <- Resource.eval(ServerMiddleware(domain, ssoClient, userSvc))
routes <- GraphQLRoutes(ssoClient, pool, SkunkMonitor.noopMonitor[F], GraphQLServiceTTL)
routes <- GraphQLRoutes(ssoClient, pool, SkunkMonitor.noopMonitor[F], GraphQLServiceTTL, userSvc)
} yield { wsb =>
middleware(routes(wsb))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ trait BaseMapping[F[_]]
lazy val ClassicalType = schema.ref("Classical")
lazy val CloudExtinctionType = schema.ref("CloudExtinction")
lazy val ConstraintSetType = schema.ref("ConstraintSet")
lazy val ConstraintSetGroupType = schema.ref("ConstraintSetGroup")
lazy val ConstraintSetGroupSelectResultType = schema.ref("ConstraintSetGroupSelectResult")
lazy val CoordinatesType = schema.ref("Coordinates")
lazy val CreateObservationResultType = schema.ref("CreateObservationResult")
lazy val CreateProgramResultType = schema.ref("CreateProgramResult")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import org.typelevel.log4cats.Logger
import skunk.Session

import scala.concurrent.duration._
import lucuma.odb.service.UserService

object GraphQLRoutes {

Expand All @@ -37,6 +38,7 @@ object GraphQLRoutes {
pool: Resource[F, Session[F]],
monitor: SkunkMonitor[F],
ttl: FiniteDuration,
userSvc: UserService[F],
): Resource[F, WebSocketBuilder2[F] => HttpRoutes[F]] =
OdbMapping.Topics(pool).flatMap { topics =>
Cache.timed[F, Authorization, Option[GraphQLService[F]]](ttl).map { cache => wsb =>
Expand All @@ -52,6 +54,9 @@ object GraphQLRoutes {
{
for {
user <- OptionT(client.get(a))
// If the user has never hit the ODB using http then there will be no user
// entry in the database. So go ahead and [re]canonicalize here to be sure.
_ <- OptionT.liftF(userSvc.canonicalizeUser(user))
map <- OptionT.liftF(OdbMapping(pool, monitor, user, topics))
svc = new GrackleGraphQLService(map)
} yield svc
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ object OdbMapping {
with AllocationMapping[F]
with AngleMapping[F]
with CatalogInfoMapping[F]
with ConstraintSetGroupMapping[F]
with ConstraintSetGroupSelectResultMapping[F]
with ConstraintSetMapping[F]
with CoordinatesMapping[F]
with CreateObservationResultMapping[F]
Expand Down Expand Up @@ -160,6 +162,8 @@ object OdbMapping {
AllocationMapping,
AngleMapping,
CatalogInfoMapping,
ConstraintSetGroupMapping,
ConstraintSetGroupSelectResultMapping,
ConstraintSetMapping,
CoordinatesMapping,
CreateObservationResultMapping,
Expand Down Expand Up @@ -214,6 +218,7 @@ object OdbMapping {
override val selectElaborator: SelectElaborator =
SelectElaborator(
List(
ConstraintSetGroupElaborator,
MutationElaborator,
ProgramElaborator,
SubscriptionElaborator,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) 2016-2022 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package lucuma.odb.graphql

package mapping

import cats.syntax.all._
import edu.gemini.grackle.skunk.SkunkMapping
import edu.gemini.grackle.Mapping
import edu.gemini.grackle.Path
import edu.gemini.grackle.Predicate
import edu.gemini.grackle.Predicate._
import edu.gemini.grackle.Query
import edu.gemini.grackle.Query._
import edu.gemini.grackle.Result
import edu.gemini.grackle.TypeRef
import edu.gemini.grackle.skunk.SkunkMapping
import lucuma.core.model.Observation
import lucuma.core.model.Program
import lucuma.core.model.User
import lucuma.odb.data.Existence
import lucuma.odb.graphql.predicate.Predicates
import table.ObservationView
import lucuma.odb.graphql.table.ConstraintSetGroupView
import binding._
import input._
import table._

trait ConstraintSetGroupMapping[F[_]]
extends ConstraintSetGroupView[F]
with ObservationView[F]
with Predicates[F] {

lazy val ConstraintSetGroupMapping =
ObjectMapping(
tpe = ConstraintSetGroupType,
fieldMappings = List(
SqlField("key", ConstraintSetGroupView.ConstraintSetKey, key = true, hidden = true),
SqlField("programId", ConstraintSetGroupView.ProgramId),
SqlObject("observations", Join(ConstraintSetGroupView.ConstraintSetKey, ObservationView.ConstraintSet.Key)),
SqlObject("constraintSet", Join(ConstraintSetGroupView.ObservationId, ObservationView.Id)),
)
)

lazy val ConstraintSetGroupElaborator: Map[TypeRef, PartialFunction[Select, Result[Query]]] =
Map(
ConstraintSetGroupType -> {
case Select("observations", List(
BooleanBinding("includeDeleted", rIncludeDeleted),
ObservationIdBinding.Option("OFFSET", rOFFSET),
NonNegIntBinding.Option("LIMIT", rLIMIT),
), child) =>
(rIncludeDeleted, rOFFSET, rLIMIT).parTupled.flatMap { (includeDeleted, OFFSET, lim) =>
val limit = lim.fold(ResultMapping.MaxLimit)(_.value)
ResultMapping.selectResult("observations", child, limit) { q =>
FilterOrderByOffsetLimit(
pred = Some(and(List(
Predicates.observation.existence.includeDeleted(includeDeleted),
OFFSET.fold[Predicate](True)(Predicates.observation.id.gtEql)
))),
oss = Some(List(OrderSelection[Observation.Id](ObservationType / "id", true, true))),
offset = None,
limit = Some(limit + 1),
q
)
}
}
}
)
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright (c) 2016-2022 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package lucuma.odb.graphql

package mapping

import edu.gemini.grackle.skunk.SkunkMapping

import table.ObservationView
import lucuma.odb.graphql.table.ConstraintSetGroupView

trait ConstraintSetGroupSelectResultMapping[F[_]] extends ResultMapping[F] {

lazy val ConstraintSetGroupSelectResultMapping: ObjectMapping =
topLevelSelectResultMapping(ConstraintSetGroupSelectResultType)

}
Original file line number Diff line number Diff line change
Expand Up @@ -11,16 +11,18 @@ import lucuma.odb.graphql.table.ProgramTable
import skunk.codec.numeric.int8

import scala.tools.util.PathResolver.Environment
import lucuma.odb.graphql.table.ConstraintSetGroupView

trait ObservationSelectResultMapping[F[_]]
extends ObservationView[F] with ProgramTable[F] with ResultMapping[F] {
extends ConstraintSetGroupView[F] with ObservationView[F] with ProgramTable[F] with ResultMapping[F] {

lazy val ObservationSelectResultMapping: TypeMapping =
SwitchMapping(
ObservationSelectResultType,
List(
(QueryType, "observations", topLevelSelectResultMapping(ObservationSelectResultType)),
(ProgramType, "observations", nestedSelectResultMapping(ObservationSelectResultType, ProgramTable.Id, Join(ProgramTable.Id, ObservationView.ProgramId))),
(ConstraintSetGroupType, "observations", nestedSelectResultMapping(ObservationSelectResultType, ConstraintSetGroupView.ConstraintSetKey, Join(ConstraintSetGroupView.ConstraintSetKey, ObservationView.ConstraintSet.Key))),
)
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ trait QueryMapping[F[_]] extends Predicates[F] {
ObjectMapping(
tpe = QueryType,
fieldMappings = List(
SqlObject("constraintSetGroup"),
SqlObject("filterTypeMeta"),
SqlObject("observation"),
SqlObject("observations"),
Expand All @@ -56,6 +57,7 @@ trait QueryMapping[F[_]] extends Predicates[F] {

lazy val QueryElaborator: Map[TypeRef, PartialFunction[Select, Result[Query]]] =
List(
ConstraintSetGroup,
FilterTypeMeta,
Observation,
Observations,
Expand All @@ -70,6 +72,35 @@ trait QueryMapping[F[_]] extends Predicates[F] {

// Elaborators below

private lazy val ConstraintSetGroup: PartialFunction[Select, Result[Query]] = {
case Select("constraintSetGroup", List(
ProgramIdBinding("programId", rProgramId),
_, // TODO: WHERE
NonNegIntBinding.Option("LIMIT", rLIMIT),
BooleanBinding("includeDeleted", rIncludeDeleted)
), child) =>
(rProgramId, rLIMIT, rIncludeDeleted).parTupled.flatMap { (pid, LIMIT, includeDeleted) =>
val limit = LIMIT.foldLeft(ResultMapping.MaxLimit)(_ min _.value)
ResultMapping.selectResult("constraintSetGroup", child, limit) { q =>
FilterOrderByOffsetLimit(
pred = Some(
and(List(
Predicates.constraintSetGroup.programId.eql(pid),
// Predicates.constraintSetGroup.programId.existence.includeDeleted(includeDeleted),
// Predicates.constraintSetGroup.programId.isVisibleTo(user),
))
),
oss = Some(List(
OrderSelection[String](ConstraintSetGroupType / "key")
)),
offset = None,
limit = Some(limit + 1), // Select one extra row here.
child = q
)
}
}
}

private lazy val FilterTypeMeta: PartialFunction[Select, Result[Query]] =
case Select("filterTypeMeta", Nil, child) =>
Result(Select("filterTypeMeta", Nil,
Expand Down Expand Up @@ -98,16 +129,18 @@ trait QueryMapping[F[_]] extends Predicates[F] {
val WhereObservationBinding = WhereObservation.binding(Path.from(ObservationType))
{
case Select("observations", List(
ProgramIdBinding.Option("programId", rPid),
WhereObservationBinding.Option("WHERE", rWHERE),
ObservationIdBinding.Option("OFFSET", rOFFSET),
NonNegIntBinding.Option("LIMIT", rLIMIT),
BooleanBinding("includeDeleted", rIncludeDeleted)
), child) =>
(rWHERE, rOFFSET, rLIMIT, rIncludeDeleted).parTupled.flatMap { (WHERE, OFFSET, LIMIT, includeDeleted) =>
(rPid, rWHERE, rOFFSET, rLIMIT, rIncludeDeleted).parTupled.flatMap { (pid, WHERE, OFFSET, LIMIT, includeDeleted) =>
val limit = LIMIT.foldLeft(ResultMapping.MaxLimit)(_ min _.value)
ResultMapping.selectResult("observations", child, limit) { q =>
FilterOrderByOffsetLimit(
pred = Some(and(List(
pid.map(Predicates.observation.program.id.eql).getOrElse(True),
OFFSET.map(Predicates.observation.id.gtEql).getOrElse(True),
Predicates.observation.existence.includeDeleted(includeDeleted),
Predicates.observation.program.isVisibleTo(user),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright (c) 2016-2022 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package lucuma.odb.graphql.predicate

import edu.gemini.grackle.Path
import lucuma.core.model.Program

class ConstraintSetGroupPredicates(path: Path) {
val programId = LeafPredicates[Program.Id](path / "programId")
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ trait Predicates[F[_]] extends BaseMapping[F] {
val proposalClass = ProposalClassPredicates(Path.from(ProposalClassType))
val setAllocationResult = SetAllocationResultPredicates(Path.from(SetAllocationResultType))
val target = TargetPredicates(Path.from(TargetType))
val constraintSetGroup = ConstraintSetGroupPredicates(Path.from(ConstraintSetGroupType))
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) 2016-2022 Association of Universities for Research in Astronomy, Inc. (AURA)
// For license information see LICENSE or https://opensource.org/licenses/BSD-3-Clause

package lucuma.odb.graphql

package table

import edu.gemini.grackle.skunk.SkunkMapping
import lucuma.odb.util.Codecs._
import skunk.codec.all._

trait ConstraintSetGroupView[F[_]] extends BaseMapping[F] {

object ConstraintSetGroupView extends TableDef("v_constraint_set_group") {
val ProgramId: ColumnRef = col("c_program_id", program_id)
val ConstraintSetKey: ColumnRef = col("c_conditions_key", text)
val ObservationId: ColumnRef = col("c_observation_id", observation_id)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ trait ObservationView[F[_]] extends BaseMapping[F] {
}

object ConstraintSet {
val Key: ColumnRef = col("c_conditions_key", text)
val CloudExtinction: ColumnRef = col("c_cloud_extinction", cloud_extinction.embedded)
val ImageQuality: ColumnRef = col("c_image_quality", image_quality.embedded)
val SkyBackground: ColumnRef = col("c_sky_background", sky_background.embedded)
Expand Down

0 comments on commit 831e788

Please sign in to comment.