Validate fact requirements through briefing vocabulary
This commit is contained in:
@@ -28,8 +28,8 @@ type ModuleDefinition struct {
|
||||
ID module.ID
|
||||
StanzaName string
|
||||
DefaultOptions any
|
||||
RequiredCollected []factRequirement
|
||||
RequiredDerived []factRequirement
|
||||
RequiredCollected []*factRequirement
|
||||
RequiredDerived []*factRequirement
|
||||
SupportedReports []report.ID
|
||||
MissingData module.MissingDataBehavior
|
||||
AllowDuplicate bool
|
||||
@@ -76,6 +76,9 @@ func NewModuleRegistry(definitions []ModuleDefinition) (ModuleRegistry, error) {
|
||||
if definition.MissingData == module.MissingDataWarn {
|
||||
return ModuleRegistry{}, fmt.Errorf("module %q uses unsupported missing data behavior %q", definition.ID, definition.MissingData)
|
||||
}
|
||||
if err := validateFactRequirements(definition); err != nil {
|
||||
return ModuleRegistry{}, err
|
||||
}
|
||||
if existingID, ok := seenStanzas[definition.StanzaName]; ok {
|
||||
return ModuleRegistry{}, fmt.Errorf("duplicate stanza name %q for modules %q and %q", definition.StanzaName, existingID, definition.ID)
|
||||
}
|
||||
@@ -153,60 +156,48 @@ func (r ModuleRegistry) BuildModule(ctx ModuleContext, item module.ConfigItem) (
|
||||
func missingRequirements(definition ModuleDefinition, ctx ModuleContext) []string {
|
||||
var missing []string
|
||||
for _, requirement := range definition.RequiredCollected {
|
||||
if !collectedFactAvailable(requirement, ctx) {
|
||||
if !requirement.available(ctx) {
|
||||
missing = append(missing, requirement.String())
|
||||
}
|
||||
}
|
||||
for _, requirement := range definition.RequiredDerived {
|
||||
if !derivedFactAvailable(requirement, ctx) {
|
||||
if !requirement.available(ctx) {
|
||||
missing = append(missing, requirement.String())
|
||||
}
|
||||
}
|
||||
return missing
|
||||
}
|
||||
|
||||
func collectedFactAvailable(requirement factRequirement, ctx ModuleContext) bool {
|
||||
switch requirement {
|
||||
case currentConditionsRequirement:
|
||||
return ctx.Collected.Current != nil
|
||||
case narrativeForecastRequirement:
|
||||
return ctx.Collected.Narrative != nil
|
||||
case hourlyForecastRequirement:
|
||||
return ctx.Collected.Hourly != nil
|
||||
case alertsRequirement:
|
||||
return ctx.Collected.Alerts != nil
|
||||
case discussionRequirement:
|
||||
return ctx.Collected.Discussion != nil
|
||||
case weatherStoryRequirement:
|
||||
return ctx.Collected.WeatherStory != nil
|
||||
case spcOutlooksRequirement:
|
||||
return ctx.Collected.SPCConvectiveOutlooks != nil
|
||||
case sourceMetadataRequirement:
|
||||
return len(ctx.Collected.SourceProvenance) > 0 || len(ctx.Collected.SourceWarnings) > 0
|
||||
default:
|
||||
return false
|
||||
func validateFactRequirements(definition ModuleDefinition) error {
|
||||
if err := validateFactRequirementCategory(definition.ID, definition.RequiredCollected, collectedFactRequirement); err != nil {
|
||||
return err
|
||||
}
|
||||
return validateFactRequirementCategory(definition.ID, definition.RequiredDerived, derivedFactRequirement)
|
||||
}
|
||||
|
||||
func derivedFactAvailable(requirement factRequirement, ctx ModuleContext) bool {
|
||||
switch requirement {
|
||||
case hourlyPeriodsRequirement:
|
||||
return len(ctx.Derived.ValidPeriodHourlyPeriods) > 0
|
||||
case narrativePeriodsRequirement:
|
||||
return len(ctx.Derived.ValidPeriodNarrativePeriods) > 0
|
||||
case alertOverlapsRequirement:
|
||||
return true
|
||||
case dailySummariesRequirement:
|
||||
return len(ctx.Derived.DailySummaries) > 0
|
||||
case daypartSummariesRequirement:
|
||||
return len(ctx.Derived.DaypartSummaries) > 0
|
||||
case precipTimingRequirement:
|
||||
return true
|
||||
case spcDerivedOutlooksRequirement:
|
||||
return ctx.Derived.SPCConvectiveOutlooks != nil
|
||||
default:
|
||||
return false
|
||||
func validateFactRequirementCategory(moduleID module.ID, requirements []*factRequirement, want factRequirementCategory) error {
|
||||
for _, requirement := range requirements {
|
||||
if requirement == nil {
|
||||
return fmt.Errorf("module %q uses unknown %s fact requirement %q", moduleID, want, "")
|
||||
}
|
||||
descriptor, ok := lookupFactRequirement(requirement.identity)
|
||||
if !ok || descriptor != requirement {
|
||||
return fmt.Errorf("module %q uses unknown %s fact requirement %q", moduleID, want, requirement.identity)
|
||||
}
|
||||
if descriptor.category != want {
|
||||
return fmt.Errorf("module %q lists %s fact requirement %q as %s", moduleID, descriptor.category, descriptor.identity, want)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func lookupFactRequirement(identity string) (*factRequirement, bool) {
|
||||
for _, requirement := range factRequirementVocabulary {
|
||||
if requirement.identity == identity {
|
||||
return requirement, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
func (r ModuleRegistry) ValidateComposition(reportID report.ID, items []module.ConfigItem) error {
|
||||
@@ -291,7 +282,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.Metadata,
|
||||
StanzaName: "metadata",
|
||||
DefaultOptions: module.MetadataOptions{},
|
||||
RequiredCollected: []factRequirement{sourceMetadataRequirement},
|
||||
RequiredCollected: []*factRequirement{sourceMetadataRequirement},
|
||||
SupportedReports: allReports,
|
||||
MissingData: module.MissingDataEmpty,
|
||||
Builder: buildMetadataModule,
|
||||
@@ -300,7 +291,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.CurrentConditions,
|
||||
StanzaName: "current_conditions",
|
||||
DefaultOptions: module.CurrentConditionsOptions{},
|
||||
RequiredCollected: []factRequirement{currentConditionsRequirement},
|
||||
RequiredCollected: []*factRequirement{currentConditionsRequirement},
|
||||
SupportedReports: allReports,
|
||||
MissingData: module.MissingDataOmit,
|
||||
Builder: buildCurrentConditionsModule,
|
||||
@@ -310,8 +301,8 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.NarrativeForecast,
|
||||
StanzaName: "narrative_forecast",
|
||||
DefaultOptions: module.NarrativeForecastOptions{},
|
||||
RequiredCollected: []factRequirement{narrativeForecastRequirement},
|
||||
RequiredDerived: []factRequirement{narrativePeriodsRequirement},
|
||||
RequiredCollected: []*factRequirement{narrativeForecastRequirement},
|
||||
RequiredDerived: []*factRequirement{narrativePeriodsRequirement},
|
||||
SupportedReports: []report.ID{report.Daily, report.Today, report.Tomorrow},
|
||||
MissingData: module.MissingDataOmit,
|
||||
Builder: buildNarrativeForecastModule,
|
||||
@@ -320,8 +311,8 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.HourlyForecast,
|
||||
StanzaName: "hourly_forecast",
|
||||
DefaultOptions: module.HourlyForecastOptions{},
|
||||
RequiredCollected: []factRequirement{hourlyForecastRequirement},
|
||||
RequiredDerived: []factRequirement{hourlyPeriodsRequirement},
|
||||
RequiredCollected: []*factRequirement{hourlyForecastRequirement},
|
||||
RequiredDerived: []*factRequirement{hourlyPeriodsRequirement},
|
||||
SupportedReports: []report.ID{report.Daily, report.Today, report.Tomorrow, report.Hourly},
|
||||
MissingData: module.MissingDataOmit,
|
||||
Builder: buildHourlyForecastModule,
|
||||
@@ -331,7 +322,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.DerivedDailySummary,
|
||||
StanzaName: "derived_daily_summary",
|
||||
DefaultOptions: module.DerivedDailySummaryOptions{},
|
||||
RequiredDerived: []factRequirement{dailySummariesRequirement, precipTimingRequirement},
|
||||
RequiredDerived: []*factRequirement{dailySummariesRequirement, precipTimingRequirement},
|
||||
SupportedReports: []report.ID{report.Daily, report.Today, report.Tomorrow},
|
||||
MissingData: module.MissingDataError,
|
||||
Builder: buildDerivedDailySummaryModule,
|
||||
@@ -340,7 +331,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.DerivedDaypartSummaries,
|
||||
StanzaName: "derived_daypart_summaries",
|
||||
DefaultOptions: module.DerivedDaypartSummariesOptions{},
|
||||
RequiredDerived: []factRequirement{daypartSummariesRequirement},
|
||||
RequiredDerived: []*factRequirement{daypartSummariesRequirement},
|
||||
SupportedReports: daypartReports,
|
||||
MissingData: module.MissingDataError,
|
||||
Builder: buildDerivedDaypartSummariesModule,
|
||||
@@ -350,7 +341,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.PrecipTiming,
|
||||
StanzaName: "precip_timing",
|
||||
DefaultOptions: module.PrecipTimingOptions{},
|
||||
RequiredDerived: []factRequirement{precipTimingRequirement},
|
||||
RequiredDerived: []*factRequirement{precipTimingRequirement},
|
||||
SupportedReports: allReports,
|
||||
MissingData: module.MissingDataEmpty,
|
||||
Builder: buildPrecipTimingModule,
|
||||
@@ -359,8 +350,8 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.AlertDigest,
|
||||
StanzaName: "alert_digest",
|
||||
DefaultOptions: module.AlertDigestOptions{},
|
||||
RequiredCollected: []factRequirement{alertsRequirement},
|
||||
RequiredDerived: []factRequirement{alertOverlapsRequirement},
|
||||
RequiredCollected: []*factRequirement{alertsRequirement},
|
||||
RequiredDerived: []*factRequirement{alertOverlapsRequirement},
|
||||
SupportedReports: allReports,
|
||||
MissingData: module.MissingDataEmpty,
|
||||
Builder: buildAlertDigestModule,
|
||||
@@ -369,8 +360,8 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.SPCConvectiveOutlooks,
|
||||
StanzaName: string(module.SPCConvectiveOutlooks),
|
||||
DefaultOptions: module.SPCConvectiveOutlooksOptions{},
|
||||
RequiredCollected: []factRequirement{spcOutlooksRequirement},
|
||||
RequiredDerived: []factRequirement{spcDerivedOutlooksRequirement},
|
||||
RequiredCollected: []*factRequirement{spcOutlooksRequirement},
|
||||
RequiredDerived: []*factRequirement{spcDerivedOutlooksRequirement},
|
||||
SupportedReports: allReports,
|
||||
MissingData: module.MissingDataEmpty,
|
||||
Builder: buildSPCConvectiveOutlooksModule,
|
||||
@@ -379,7 +370,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.AreaForecastDiscussion,
|
||||
StanzaName: "area_forecast_discussion",
|
||||
DefaultOptions: module.AreaForecastDiscussionOptions{},
|
||||
RequiredCollected: []factRequirement{discussionRequirement},
|
||||
RequiredCollected: []*factRequirement{discussionRequirement},
|
||||
SupportedReports: allReports,
|
||||
MissingData: module.MissingDataOmit,
|
||||
Builder: buildAreaForecastDiscussionModule,
|
||||
@@ -388,8 +379,8 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.SPCConvectiveDiscussion,
|
||||
StanzaName: string(module.SPCConvectiveDiscussion),
|
||||
DefaultOptions: module.SPCConvectiveDiscussionOptions{},
|
||||
RequiredCollected: []factRequirement{spcOutlooksRequirement},
|
||||
RequiredDerived: []factRequirement{spcDerivedOutlooksRequirement},
|
||||
RequiredCollected: []*factRequirement{spcOutlooksRequirement},
|
||||
RequiredDerived: []*factRequirement{spcDerivedOutlooksRequirement},
|
||||
SupportedReports: allReports,
|
||||
MissingData: module.MissingDataOmit,
|
||||
Builder: buildSPCConvectiveDiscussionModule,
|
||||
@@ -398,7 +389,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.WeatherStory,
|
||||
StanzaName: "weather_story",
|
||||
DefaultOptions: module.WeatherStoryOptions{},
|
||||
RequiredCollected: []factRequirement{weatherStoryRequirement},
|
||||
RequiredCollected: []*factRequirement{weatherStoryRequirement},
|
||||
SupportedReports: allReports,
|
||||
MissingData: module.MissingDataOmit,
|
||||
Builder: buildWeatherStoryModule,
|
||||
@@ -407,7 +398,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.OutdoorWindows,
|
||||
StanzaName: "outdoor_windows",
|
||||
DefaultOptions: module.OutdoorWindowsOptions{},
|
||||
RequiredDerived: []factRequirement{daypartSummariesRequirement},
|
||||
RequiredDerived: []*factRequirement{daypartSummariesRequirement},
|
||||
SupportedReports: daypartReports,
|
||||
MissingData: module.MissingDataEmpty,
|
||||
Builder: buildOutdoorWindowsModule,
|
||||
@@ -416,7 +407,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.TodayPlanning,
|
||||
StanzaName: "today_planning",
|
||||
DefaultOptions: module.TodayPlanningOptions{},
|
||||
RequiredDerived: []factRequirement{dailySummariesRequirement},
|
||||
RequiredDerived: []*factRequirement{dailySummariesRequirement},
|
||||
SupportedReports: []report.ID{report.Today},
|
||||
MissingData: module.MissingDataEmpty,
|
||||
Builder: buildTodayPlanningModule,
|
||||
@@ -425,7 +416,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.TomorrowPlanning,
|
||||
StanzaName: "tomorrow_planning",
|
||||
DefaultOptions: module.TomorrowPlanningOptions{},
|
||||
RequiredDerived: []factRequirement{dailySummariesRequirement},
|
||||
RequiredDerived: []*factRequirement{dailySummariesRequirement},
|
||||
SupportedReports: []report.ID{report.Tomorrow},
|
||||
MissingData: module.MissingDataEmpty,
|
||||
Builder: buildTomorrowPlanningModule,
|
||||
@@ -434,7 +425,7 @@ func defaultModuleDefinitions() []ModuleDefinition {
|
||||
ID: module.DailyPlanning,
|
||||
StanzaName: "daily_planning",
|
||||
DefaultOptions: module.DailyPlanningOptions{},
|
||||
RequiredDerived: []factRequirement{dailySummariesRequirement},
|
||||
RequiredDerived: []*factRequirement{dailySummariesRequirement},
|
||||
SupportedReports: []report.ID{report.Daily},
|
||||
MissingData: module.MissingDataEmpty,
|
||||
Builder: buildDailyPlanningModule,
|
||||
|
||||
Reference in New Issue
Block a user