mirror of
https://github.com/gocsaf/csaf.git
synced 2025-12-22 11:55:40 +01:00
Fix golint warnings.
This commit is contained in:
parent
6b05323271
commit
04fb8f7e75
6 changed files with 122 additions and 49 deletions
|
|
@ -248,7 +248,7 @@ func (c *controller) upload(rw http.ResponseWriter, r *http.Request) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
feedURL := csaf.JsonURL(
|
feedURL := csaf.JSONURL(
|
||||||
c.cfg.Domain + "/.well-known/csaf/" + ts + "/" + feedName)
|
c.cfg.Domain + "/.well-known/csaf/" + ts + "/" + feedName)
|
||||||
|
|
||||||
tlpLabel := csaf.TLPLabel(strings.ToUpper(ts))
|
tlpLabel := csaf.TLPLabel(strings.ToUpper(ts))
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,7 @@ const (
|
||||||
type extraction struct {
|
type extraction struct {
|
||||||
id string
|
id string
|
||||||
title string
|
title string
|
||||||
publisher *csaf.CSAFPublisher
|
publisher *csaf.Publisher
|
||||||
initialReleaseDate time.Time
|
initialReleaseDate time.Time
|
||||||
currentReleaseDate time.Time
|
currentReleaseDate time.Time
|
||||||
summary string
|
summary string
|
||||||
|
|
@ -109,7 +109,7 @@ func (e *extraction) extractPublisher(path extractFunc) error {
|
||||||
if err := enc.Encode(p); err != nil {
|
if err := enc.Encode(p); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
e.publisher = new(csaf.CSAFPublisher)
|
e.publisher = new(csaf.Publisher)
|
||||||
if err := json.Unmarshal(buf.Bytes(), e.publisher); err != nil {
|
if err := json.Unmarshal(buf.Bytes(), e.publisher); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,8 @@ func createSecurity(c *config) error {
|
||||||
f, "CSAF: %s/.well-known/csaf/provider-metadata.json\n",
|
f, "CSAF: %s/.well-known/csaf/provider-metadata.json\n",
|
||||||
c.Domain)
|
c.Domain)
|
||||||
return f.Close()
|
return f.Close()
|
||||||
} else {
|
|
||||||
return err
|
|
||||||
}
|
}
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ func newProviderMetadata(cfg *config) *csaf.ProviderMetadata {
|
||||||
var (
|
var (
|
||||||
ts = string(t)
|
ts = string(t)
|
||||||
feedName = "csaf-feed-tlp-" + ts + ".json"
|
feedName = "csaf-feed-tlp-" + ts + ".json"
|
||||||
feedURL = csaf.JsonURL(
|
feedURL = csaf.JSONURL(
|
||||||
cfg.Domain + "/.well-known/csaf/" + ts + "/" + feedName)
|
cfg.Domain + "/.well-known/csaf/" + ts + "/" + feedName)
|
||||||
tlpLabel = csaf.TLPLabel(strings.ToUpper(ts))
|
tlpLabel = csaf.TLPLabel(strings.ToUpper(ts))
|
||||||
)
|
)
|
||||||
|
|
|
||||||
133
csaf/models.go
133
csaf/models.go
|
|
@ -10,14 +10,20 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// TLPLabel is the traffic light policy of the CSAF.
|
||||||
type TLPLabel string
|
type TLPLabel string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// TLPLabelUnlabeled is the 'UNLABELED' policy.
|
||||||
TLPLabelUnlabeled = "UNLABELED"
|
TLPLabelUnlabeled = "UNLABELED"
|
||||||
TLPLabelWhite = "WHITE"
|
// TLPLabelWhite is the 'WHITE' policy.
|
||||||
TLPLabelGreen = "GREEN"
|
TLPLabelWhite = "WHITE"
|
||||||
TLPLabelAmber = "AMBER"
|
// TLPLabelGreen is the 'GREEN' policy.
|
||||||
TLPLabelRed = "RED"
|
TLPLabelGreen = "GREEN"
|
||||||
|
// TLPLabelAmber is the 'AMBER' policy.
|
||||||
|
TLPLabelAmber = "AMBER"
|
||||||
|
// TLPLabelRed is the 'RED' policy.
|
||||||
|
TLPLabelRed = "RED"
|
||||||
)
|
)
|
||||||
|
|
||||||
var tlpLabelPattern = alternativesUnmarshal(
|
var tlpLabelPattern = alternativesUnmarshal(
|
||||||
|
|
@ -27,47 +33,63 @@ var tlpLabelPattern = alternativesUnmarshal(
|
||||||
string("AMBER"),
|
string("AMBER"),
|
||||||
string("RED"))
|
string("RED"))
|
||||||
|
|
||||||
type JsonURL string
|
// JSONURL is an URL to JSON document.
|
||||||
|
type JSONURL string
|
||||||
|
|
||||||
var jsonURLPattern = patternUnmarshal(`\.json$`)
|
var jsonURLPattern = patternUnmarshal(`\.json$`)
|
||||||
|
|
||||||
|
// Feed is CSAF feed.
|
||||||
type Feed struct {
|
type Feed struct {
|
||||||
Summary string `json:"summary"`
|
Summary string `json:"summary"`
|
||||||
TLPLabel *TLPLabel `json:"tlp_label"` // required
|
TLPLabel *TLPLabel `json:"tlp_label"` // required
|
||||||
URL *JsonURL `json:"url"` // required
|
URL *JSONURL `json:"url"` // required
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ROLIE is the ROLIE extension of the CSAF feed.
|
||||||
type ROLIE struct {
|
type ROLIE struct {
|
||||||
Categories []JsonURL `json:"categories,omitempty"`
|
Categories []JSONURL `json:"categories,omitempty"`
|
||||||
Feeds []Feed `json:"feeds"` // required
|
Feeds []Feed `json:"feeds"` // required
|
||||||
Services []JsonURL `json:"services,omitempty"`
|
Services []JSONURL `json:"services,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Distribution is a distribution of a CSAF feed.
|
||||||
type Distribution struct {
|
type Distribution struct {
|
||||||
DirectoryURL string `json:"directory_url,omitempty"`
|
DirectoryURL string `json:"directory_url,omitempty"`
|
||||||
Rolie []ROLIE `json:"rolie"`
|
Rolie []ROLIE `json:"rolie"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TimeStamp represents a time stamp in a CSAF feed.
|
||||||
type TimeStamp time.Time
|
type TimeStamp time.Time
|
||||||
|
|
||||||
|
// Fingerprint is the fingerprint of a OpenPGP key used to sign
|
||||||
|
// the CASF documents.
|
||||||
type Fingerprint string
|
type Fingerprint string
|
||||||
|
|
||||||
var fingerprintPattern = patternUnmarshal(`^[0-9a-fA-F]{40,}$`)
|
var fingerprintPattern = patternUnmarshal(`^[0-9a-fA-F]{40,}$`)
|
||||||
|
|
||||||
|
// PGPKey is location and the fingerprint of the key
|
||||||
|
// used to sign the CASF documents.
|
||||||
type PGPKey struct {
|
type PGPKey struct {
|
||||||
Fingerprint Fingerprint `json:"fingerprint,omitempty"`
|
Fingerprint Fingerprint `json:"fingerprint,omitempty"`
|
||||||
URL *string `json:"url"` // required
|
URL *string `json:"url"` // required
|
||||||
}
|
}
|
||||||
|
|
||||||
type CSAFCategory string
|
// Category is the category of the CSAF feed.
|
||||||
|
type Category string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
CSAFCategoryCoordinator CSAFCategory = "coordinator"
|
// CSAFCategoryCoordinator is the "coordinator" category.
|
||||||
CSAFCategoryDiscoverer CSAFCategory = "discoverer"
|
CSAFCategoryCoordinator Category = "coordinator"
|
||||||
CSAFCategoryOther CSAFCategory = "other"
|
// CSAFCategoryDiscoverer is the "discoverer" category.
|
||||||
CSAFCategoryTranslator CSAFCategory = "translator"
|
CSAFCategoryDiscoverer Category = "discoverer"
|
||||||
CSAFCategoryUser CSAFCategory = "user"
|
// CSAFCategoryOther is the "other" category.
|
||||||
CSAFCategoryVendor CSAFCategory = "vendor"
|
CSAFCategoryOther Category = "other"
|
||||||
|
// CSAFCategoryTranslator is the "translator" category.
|
||||||
|
CSAFCategoryTranslator Category = "translator"
|
||||||
|
// CSAFCategoryUser is the "user" category.
|
||||||
|
CSAFCategoryUser Category = "user"
|
||||||
|
// CSAFCategoryVendor is the "vendor" category.
|
||||||
|
CSAFCategoryVendor Category = "vendor"
|
||||||
)
|
)
|
||||||
|
|
||||||
var csafCategoryPattern = alternativesUnmarshal(
|
var csafCategoryPattern = alternativesUnmarshal(
|
||||||
|
|
@ -78,25 +100,32 @@ var csafCategoryPattern = alternativesUnmarshal(
|
||||||
string(CSAFCategoryUser),
|
string(CSAFCategoryUser),
|
||||||
string(CSAFCategoryVendor))
|
string(CSAFCategoryVendor))
|
||||||
|
|
||||||
type CSAFPublisher struct {
|
// Publisher is the publisher of the feed.
|
||||||
Category *CSAFCategory `json:"category"` // required
|
type Publisher struct {
|
||||||
Name *string `json:"name"` // required
|
Category *Category `json:"category"` // required
|
||||||
Namespace *string `json:"namespace"` // required
|
Name *string `json:"name"` // required
|
||||||
ContactDetails string `json:"contact_details,omitempty"`
|
Namespace *string `json:"namespace"` // required
|
||||||
IssuingAuthority string `json:"issuing_authority,omitempty"`
|
ContactDetails string `json:"contact_details,omitempty"`
|
||||||
|
IssuingAuthority string `json:"issuing_authority,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MetadataVersion is the metadata version of the feed.
|
||||||
type MetadataVersion string
|
type MetadataVersion string
|
||||||
|
|
||||||
|
// MetadataVersion20 is the current version of the schema.
|
||||||
const MetadataVersion20 MetadataVersion = "2.0"
|
const MetadataVersion20 MetadataVersion = "2.0"
|
||||||
|
|
||||||
var metadataVersionPattern = alternativesUnmarshal(string(MetadataVersion20))
|
var metadataVersionPattern = alternativesUnmarshal(string(MetadataVersion20))
|
||||||
|
|
||||||
|
// MetadataRole is the role of the feed.
|
||||||
type MetadataRole string
|
type MetadataRole string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
MetadataRolePublisher MetadataRole = "csaf_publisher"
|
// MetadataRolePublisher is the "csaf_publisher" role.
|
||||||
MetadataRoleProvider MetadataRole = "csaf_provider"
|
MetadataRolePublisher MetadataRole = "csaf_publisher"
|
||||||
|
// MetadataRoleProvider is the "csaf_provider" role.
|
||||||
|
MetadataRoleProvider MetadataRole = "csaf_provider"
|
||||||
|
// MetadataRoleTrustedProvider is the "csaf_trusted_provider" role.
|
||||||
MetadataRoleTrustedProvider MetadataRole = "csaf_trusted_provider"
|
MetadataRoleTrustedProvider MetadataRole = "csaf_trusted_provider"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -105,10 +134,12 @@ var metadataRolePattern = alternativesUnmarshal(
|
||||||
string(MetadataRoleProvider),
|
string(MetadataRoleProvider),
|
||||||
string(MetadataRoleTrustedProvider))
|
string(MetadataRoleTrustedProvider))
|
||||||
|
|
||||||
|
// ProviderURL is the URL of the provider document.
|
||||||
type ProviderURL string
|
type ProviderURL string
|
||||||
|
|
||||||
var providerURLPattern = patternUnmarshal(`/provider-metadata\.json$`)
|
var providerURLPattern = patternUnmarshal(`/provider-metadata\.json$`)
|
||||||
|
|
||||||
|
// ProviderMetadata contains the metadata of the provider.
|
||||||
type ProviderMetadata struct {
|
type ProviderMetadata struct {
|
||||||
CanonicalURL *ProviderURL `json:"canonical_url"` // required
|
CanonicalURL *ProviderURL `json:"canonical_url"` // required
|
||||||
Distributions []Distribution `json:"distributions,omitempty"`
|
Distributions []Distribution `json:"distributions,omitempty"`
|
||||||
|
|
@ -117,7 +148,7 @@ type ProviderMetadata struct {
|
||||||
MetadataVersion *MetadataVersion `json:"metadata_version"` // required
|
MetadataVersion *MetadataVersion `json:"metadata_version"` // required
|
||||||
MirrorOnCSAFAggregators *bool `json:"mirror_on_CSAF_aggregators"` // required
|
MirrorOnCSAFAggregators *bool `json:"mirror_on_CSAF_aggregators"` // required
|
||||||
PGPKeys []PGPKey `json:"pgp_keys,omitempty"`
|
PGPKeys []PGPKey `json:"pgp_keys,omitempty"`
|
||||||
Publisher *CSAFPublisher `json:"publisher"` // required
|
Publisher *Publisher `json:"publisher"` // required
|
||||||
Role *MetadataRole `json:"role"` // required
|
Role *MetadataRole `json:"role"` // required
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -144,6 +175,7 @@ func alternativesUnmarshal(alternatives ...string) func([]byte) (string, error)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements the encoding.TextUnmarshaller interface.
|
||||||
func (tl *TLPLabel) UnmarshalText(data []byte) error {
|
func (tl *TLPLabel) UnmarshalText(data []byte) error {
|
||||||
s, err := tlpLabelPattern(data)
|
s, err := tlpLabelPattern(data)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
@ -152,14 +184,16 @@ func (tl *TLPLabel) UnmarshalText(data []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ju *JsonURL) UnmarshalText(data []byte) error {
|
// UnmarshalText implements the encoding.TextUnmarshaller interface.
|
||||||
|
func (ju *JSONURL) UnmarshalText(data []byte) error {
|
||||||
s, err := jsonURLPattern(data)
|
s, err := jsonURLPattern(data)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
*ju = JsonURL(s)
|
*ju = JSONURL(s)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements the encoding.TextUnmarshaller interface.
|
||||||
func (pu *ProviderURL) UnmarshalText(data []byte) error {
|
func (pu *ProviderURL) UnmarshalText(data []byte) error {
|
||||||
s, err := providerURLPattern(data)
|
s, err := providerURLPattern(data)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
@ -168,14 +202,16 @@ func (pu *ProviderURL) UnmarshalText(data []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cc *CSAFCategory) UnmarshalText(data []byte) error {
|
// UnmarshalText implements the encoding.TextUnmarshaller interface.
|
||||||
|
func (cc *Category) UnmarshalText(data []byte) error {
|
||||||
s, err := csafCategoryPattern(data)
|
s, err := csafCategoryPattern(data)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
*cc = CSAFCategory(s)
|
*cc = Category(s)
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements the encoding.TextUnmarshaller interface.
|
||||||
func (fp *Fingerprint) UnmarshalText(data []byte) error {
|
func (fp *Fingerprint) UnmarshalText(data []byte) error {
|
||||||
s, err := fingerprintPattern(data)
|
s, err := fingerprintPattern(data)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
|
|
@ -184,6 +220,7 @@ func (fp *Fingerprint) UnmarshalText(data []byte) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements the encoding.TextUnmarshaller interface.
|
||||||
func (ts *TimeStamp) UnmarshalText(data []byte) error {
|
func (ts *TimeStamp) UnmarshalText(data []byte) error {
|
||||||
t, err := time.Parse(time.RFC3339, string(data))
|
t, err := time.Parse(time.RFC3339, string(data))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -193,10 +230,12 @@ func (ts *TimeStamp) UnmarshalText(data []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MarshalText implements the encoding.TextMarshaller interface.
|
||||||
func (ts TimeStamp) MarshalText() ([]byte, error) {
|
func (ts TimeStamp) MarshalText() ([]byte, error) {
|
||||||
return []byte(time.Time(ts).Format(time.RFC3339)), nil
|
return []byte(time.Time(ts).Format(time.RFC3339)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Defaults fills the correct default values into the provider metadata.
|
||||||
func (pmd *ProviderMetadata) Defaults() {
|
func (pmd *ProviderMetadata) Defaults() {
|
||||||
if pmd.Role == nil {
|
if pmd.Role == nil {
|
||||||
role := MetadataRoleProvider
|
role := MetadataRoleProvider
|
||||||
|
|
@ -216,6 +255,8 @@ func (pmd *ProviderMetadata) Defaults() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate checks if the feed is valid.
|
||||||
|
// Returns an error if the validation fails otherwise nil.
|
||||||
func (f *Feed) Validate() error {
|
func (f *Feed) Validate() error {
|
||||||
switch {
|
switch {
|
||||||
case f.TLPLabel == nil:
|
case f.TLPLabel == nil:
|
||||||
|
|
@ -226,6 +267,8 @@ func (f *Feed) Validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate checks if the ROLIE extension is valid.
|
||||||
|
// Returns an error if the validation fails otherwise nil.
|
||||||
func (r *ROLIE) Validate() error {
|
func (r *ROLIE) Validate() error {
|
||||||
if len(r.Feeds) < 1 {
|
if len(r.Feeds) < 1 {
|
||||||
return errors.New("ROLIE needs at least one feed")
|
return errors.New("ROLIE needs at least one feed")
|
||||||
|
|
@ -238,7 +281,9 @@ func (r *ROLIE) Validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cp *CSAFPublisher) Validate() error {
|
// Validate checks if the publisher is valid.
|
||||||
|
// Returns an error if the validation fails otherwise nil.
|
||||||
|
func (cp *Publisher) Validate() error {
|
||||||
switch {
|
switch {
|
||||||
case cp == nil:
|
case cp == nil:
|
||||||
return errors.New("publisher is mandatory")
|
return errors.New("publisher is mandatory")
|
||||||
|
|
@ -252,6 +297,8 @@ func (cp *CSAFPublisher) Validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate checks if the PGPKey is valid.
|
||||||
|
// Returns an error if the validation fails otherwise nil.
|
||||||
func (pk *PGPKey) Validate() error {
|
func (pk *PGPKey) Validate() error {
|
||||||
if pk.URL == nil {
|
if pk.URL == nil {
|
||||||
return errors.New("pgp_key[].url is mandatory")
|
return errors.New("pgp_key[].url is mandatory")
|
||||||
|
|
@ -259,6 +306,8 @@ func (pk *PGPKey) Validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate checks if the distribution is valid.
|
||||||
|
// Returns an error if the validation fails otherwise nil.
|
||||||
func (d *Distribution) Validate() error {
|
func (d *Distribution) Validate() error {
|
||||||
for i := range d.Rolie {
|
for i := range d.Rolie {
|
||||||
if err := d.Rolie[i].Validate(); err != nil {
|
if err := d.Rolie[i].Validate(); err != nil {
|
||||||
|
|
@ -268,6 +317,8 @@ func (d *Distribution) Validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Validate checks if the provider metadata is valid.
|
||||||
|
// Returns an error if the validation fails otherwise nil.
|
||||||
func (pmd *ProviderMetadata) Validate() error {
|
func (pmd *ProviderMetadata) Validate() error {
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
|
|
@ -298,11 +349,16 @@ func (pmd *ProviderMetadata) Validate() error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetLastUpdated updates the last updated timestamp of the feed.
|
||||||
func (pmd *ProviderMetadata) SetLastUpdated(t time.Time) {
|
func (pmd *ProviderMetadata) SetLastUpdated(t time.Time) {
|
||||||
ts := TimeStamp(t.UTC())
|
ts := TimeStamp(t.UTC())
|
||||||
pmd.LastUpdated = &ts
|
pmd.LastUpdated = &ts
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SetPGP sets the fingerprint and URL of the OpenPGP key
|
||||||
|
// of the feed. If the feed already has a key with
|
||||||
|
// given fingerprint the URL updated.
|
||||||
|
// If there is no such key it is append to the list of keys.
|
||||||
func (pmd *ProviderMetadata) SetPGP(fingerprint, url string) {
|
func (pmd *ProviderMetadata) SetPGP(fingerprint, url string) {
|
||||||
for i := range pmd.PGPKeys {
|
for i := range pmd.PGPKeys {
|
||||||
if pmd.PGPKeys[i].Fingerprint == Fingerprint(fingerprint) {
|
if pmd.PGPKeys[i].Fingerprint == Fingerprint(fingerprint) {
|
||||||
|
|
@ -316,21 +372,26 @@ func (pmd *ProviderMetadata) SetPGP(fingerprint, url string) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// NewProviderMetadata creates a new provider with the given URL.
|
||||||
|
// Valid default values are set and the feed is considered to
|
||||||
|
// be updated recently.
|
||||||
func NewProviderMetadata(canonicalURL string) *ProviderMetadata {
|
func NewProviderMetadata(canonicalURL string) *ProviderMetadata {
|
||||||
pmd := new(ProviderMetadata)
|
pm := new(ProviderMetadata)
|
||||||
pmd.Defaults()
|
pm.Defaults()
|
||||||
pmd.SetLastUpdated(time.Now())
|
pm.SetLastUpdated(time.Now())
|
||||||
cu := ProviderURL(canonicalURL)
|
cu := ProviderURL(canonicalURL)
|
||||||
pmd.CanonicalURL = &cu
|
pm.CanonicalURL = &cu
|
||||||
return pmd
|
return pm
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pm *ProviderMetadata) Save(w io.Writer) error {
|
// Save saves a metadata provider to a writer.
|
||||||
|
func (pmd *ProviderMetadata) Save(w io.Writer) error {
|
||||||
enc := json.NewEncoder(w)
|
enc := json.NewEncoder(w)
|
||||||
enc.SetIndent("", " ")
|
enc.SetIndent("", " ")
|
||||||
return enc.Encode(pm)
|
return enc.Encode(pmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadProviderMetadata loads a metadata provider from a reader.
|
||||||
func LoadProviderMetadata(r io.Reader) (*ProviderMetadata, error) {
|
func LoadProviderMetadata(r io.Reader) (*ProviderMetadata, error) {
|
||||||
|
|
||||||
var pmd ProviderMetadata
|
var pmd ProviderMetadata
|
||||||
|
|
|
||||||
|
|
@ -7,30 +7,36 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Link for ROLIE.
|
||||||
type Link struct {
|
type Link struct {
|
||||||
Rel string `json:"rel"`
|
Rel string `json:"rel"`
|
||||||
HRef string `json:"href"`
|
HRef string `json:"href"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Category struct {
|
// ROLIECategory for ROLIE.
|
||||||
|
type ROLIECategory struct {
|
||||||
Scheme string `json:"scheme"`
|
Scheme string `json:"scheme"`
|
||||||
Term string `json:"term"`
|
Term string `json:"term"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Summary for ROLIE.
|
||||||
type Summary struct {
|
type Summary struct {
|
||||||
Content string `json:"content"`
|
Content string `json:"content"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Content for ROLIE.
|
||||||
type Content struct {
|
type Content struct {
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Src string `json:"src"`
|
Src string `json:"src"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Format for ROLIE.
|
||||||
type Format struct {
|
type Format struct {
|
||||||
Schema string `json:"schema"`
|
Schema string `json:"schema"`
|
||||||
Version string `json:"version"`
|
Version string `json:"version"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Entry for ROLIE.
|
||||||
type Entry struct {
|
type Entry struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Titel string `json:"title"`
|
Titel string `json:"title"`
|
||||||
|
|
@ -42,15 +48,17 @@ type Entry struct {
|
||||||
Format Format `json:"format"`
|
Format Format `json:"format"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ROLIEFeed is a ROLIE feed.
|
||||||
type ROLIEFeed struct {
|
type ROLIEFeed struct {
|
||||||
ID string `json:"id"`
|
ID string `json:"id"`
|
||||||
Title string `json:"title"`
|
Title string `json:"title"`
|
||||||
Link []Link `json:"link,omitempty"`
|
Link []Link `json:"link,omitempty"`
|
||||||
Category []Category `json:"category,omitempty"`
|
Category []ROLIECategory `json:"category,omitempty"`
|
||||||
Updated TimeStamp `json:"updated"`
|
Updated TimeStamp `json:"updated"`
|
||||||
Entry []*Entry `json:"entry,omitempty"`
|
Entry []*Entry `json:"entry,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// LoadROLIEFeed loads a ROLIE feed from a reader.
|
||||||
func LoadROLIEFeed(r io.Reader) (*ROLIEFeed, error) {
|
func LoadROLIEFeed(r io.Reader) (*ROLIEFeed, error) {
|
||||||
dec := json.NewDecoder(r)
|
dec := json.NewDecoder(r)
|
||||||
var rf ROLIEFeed
|
var rf ROLIEFeed
|
||||||
|
|
@ -60,12 +68,15 @@ func LoadROLIEFeed(r io.Reader) (*ROLIEFeed, error) {
|
||||||
return &rf, nil
|
return &rf, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Save saves a ROLIE feed to a writer.
|
||||||
func (rf *ROLIEFeed) Save(w io.Writer) error {
|
func (rf *ROLIEFeed) Save(w io.Writer) error {
|
||||||
enc := json.NewEncoder(w)
|
enc := json.NewEncoder(w)
|
||||||
enc.SetIndent("", " ")
|
enc.SetIndent("", " ")
|
||||||
return enc.Encode(rf)
|
return enc.Encode(rf)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// EntryByID looks up an entry by its ID.
|
||||||
|
// Returns nil if no such entry was found.
|
||||||
func (rf *ROLIEFeed) EntryByID(id string) *Entry {
|
func (rf *ROLIEFeed) EntryByID(id string) *Entry {
|
||||||
for _, entry := range rf.Entry {
|
for _, entry := range rf.Entry {
|
||||||
if entry.ID == id {
|
if entry.ID == id {
|
||||||
|
|
@ -75,6 +86,8 @@ func (rf *ROLIEFeed) EntryByID(id string) *Entry {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SortEntriesByUpdated sorts all the entries in the feed
|
||||||
|
// by their update times.
|
||||||
func (rf *ROLIEFeed) SortEntriesByUpdated() {
|
func (rf *ROLIEFeed) SortEntriesByUpdated() {
|
||||||
entries := rf.Entry
|
entries := rf.Entry
|
||||||
sort.Slice(entries, func(i, j int) bool {
|
sort.Slice(entries, func(i, j int) bool {
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue