Skip to content

Commit

Permalink
add atom feed
Browse files Browse the repository at this point in the history
Signed-off-by: zwwhdls <[email protected]>
  • Loading branch information
zwwhdls committed Nov 11, 2023
1 parent e4b52db commit 167d876
Show file tree
Hide file tree
Showing 4 changed files with 336 additions and 0 deletions.
139 changes: 139 additions & 0 deletions cmd/apps/apis/feed/atom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
/*
Copyright 2023 NanaFS Authors.
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 feed

import (
"encoding/xml"
"path"
"time"
)

const ns = "http://www.w3.org/2005/Atom"

type AtomXmlGenerator struct{}

func NewAtomGenerator() XmlGenerator {
return &AtomXmlGenerator{}
}

func (a *AtomXmlGenerator) Generate(f Feed) interface{} {
links := []AtomLink{}
if f.Link != nil {
links = append(links, AtomLink{Href: f.Link.Href, Rel: f.Link.Rel})
links = append(links, AtomLink{
Href: path.Join(f.Link.Href, "/atom.xml"),
Rel: "self",
})
}
author := AtomAuthor{}
if f.Author != nil {
author = AtomAuthor{
AtomPerson: AtomPerson{
Name: f.Author.Name,
Email: f.Author.Email,
},
}
}
entries := []*AtomEntry{}
for _, item := range f.Items {
link := AtomLink{}
if item.Link != nil {
link = AtomLink{
Href: item.Link.Href,
Rel: item.Link.Rel,
}
}
entries = append(entries, &AtomEntry{
Title: item.Title,
Links: []AtomLink{link},
Updated: item.Updated.Format(time.RFC3339),
Id: item.Id,
Content: &AtomContent{
Content: item.Content,
Type: "html",
},
Summary: &AtomSummary{
XMLName: xml.Name{},
Content: item.Description,
Type: "html",
},
})
}
af := &AtomFeed{
Xmlns: ns,
Title: f.Title,
Link: links,
Updated: f.Updated.Format(time.RFC3339),
Id: f.Id,
Author: &author,
Entries: entries,
}
return af
}

type AtomFeed struct {
XMLName xml.Name `xml:"feed"`
Xmlns string `xml:"xmlns,attr"`

Title string `xml:"title"` // required
Link []AtomLink
Updated string `xml:"updated"` // required
Id string `xml:"id"` // required
Author *AtomAuthor `xml:"author,omitempty"`
Entries []*AtomEntry `xml:"entry"`
}

type AtomLink struct {
//Atom 1.0 <link rel="enclosure" title="MP3" href="http://www.example.org/myaudiofile.mp3" />
XMLName xml.Name `xml:"link"`
Href string `xml:"href,attr"`
Rel string `xml:"rel,attr,omitempty"`
}

type AtomAuthor struct {
XMLName xml.Name `xml:"author"`
AtomPerson
}

type AtomPerson struct {
Name string `xml:"name,omitempty"`
Email string `xml:"email,omitempty"`
}

type AtomEntry struct {
XMLName xml.Name `xml:"entry"`
Xmlns string `xml:"xmlns,attr,omitempty"`

Title string `xml:"title"` // required
Links []AtomLink
Updated string `xml:"updated"` // required
Id string `xml:"id"` // required
Content *AtomContent
Summary *AtomSummary // required if content has src or content is base64
}

type AtomSummary struct {
XMLName xml.Name `xml:"summary"`
Content string `xml:",chardata"`
Type string `xml:"type,attr"`
}

type AtomContent struct {
XMLName xml.Name `xml:"content"`
Content string `xml:",chardata"`
Type string `xml:"type,attr"`
}
98 changes: 98 additions & 0 deletions cmd/apps/apis/feed/atom_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
Copyright 2023 NanaFS Authors.
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 feed

import (
"fmt"
"time"

. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)

var _ = Describe("TestAtomXmlGenerator", func() {
var (
atomGenerator XmlGenerator
)

BeforeEach(func() {
atomGenerator = NewAtomGenerator()
})

Context("generate atom xml", func() {
It("should be succeed", func() {
updatedStr := "2023-11-11T21:34:50+08:00"
updated, _ := time.Parse(time.RFC3339, updatedStr)

f := Feed{
Title: "hdls",
Link: &Link{Href: "https://blog.hdls.me/"},
Description: "blog of hdls",
Author: &Author{Name: "hdls"},
Updated: updated,
Id: "https://blog.hdls.me/",
Items: []*Item{
{
Title: "artitle A",
Link: &Link{Href: "https://blog.hdls.me/a.html"},
Description: "summary for A",
Id: "https://blog.hdls.me/a.html",
Updated: updated,
Content: "content for A",
},
{
Title: "artitle B",
Link: &Link{Href: "https://blog.hdls.me/b.html"},
Description: "summary for B",
Id: "https://blog.hdls.me/b.html",
Updated: updated,
Content: "content for B",
},
},
}
x, err := ToXML(atomGenerator, f)
Expect(err).Should(BeNil())
Expect(x).Should(Equal(`<?xml version="1.0" encoding="UTF-8"?><feed xmlns="http://www.w3.org/2005/Atom">
<title>hdls</title>
<link href="https://blog.hdls.me/"></link>
<link href="https:/blog.hdls.me/atom.xml" rel="self"></link>
<updated>2023-11-11T21:34:50+08:00</updated>
<id>https://blog.hdls.me/</id>
<author>
<name>hdls</name>
</author>
<entry>
<title>artitle A</title>
<link href="https://blog.hdls.me/a.html"></link>
<updated>2023-11-11T21:34:50+08:00</updated>
<id>https://blog.hdls.me/a.html</id>
<content type="html">content for A</content>
<summary type="html">summary for A</summary>
</entry>
<entry>
<title>artitle B</title>
<link href="https://blog.hdls.me/b.html"></link>
<updated>2023-11-11T21:34:50+08:00</updated>
<id>https://blog.hdls.me/b.html</id>
<content type="html">content for B</content>
<summary type="html">summary for B</summary>
</entry>
</feed>`))
fmt.Println(x)
})
})
})
67 changes: 67 additions & 0 deletions cmd/apps/apis/feed/feed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Copyright 2023 NanaFS Authors.
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 feed

import (
"encoding/xml"
"time"
)

type XmlGenerator interface {
Generate(f Feed) interface{}
}

func ToXML(generator XmlGenerator, feed Feed) (string, error) {
x := generator.Generate(feed)
data, err := xml.MarshalIndent(x, "", " ")
if err != nil {
return "", err
}
// strip empty line from default xml header
s := xml.Header[:len(xml.Header)-1] + string(data)
return s, nil
}

type Feed struct {
Title string
Link *Link
Description string
Author *Author
Updated time.Time
Created time.Time
Id string
Items []*Item
}

type Item struct {
Title string
Link *Link
Author *Author
Description string // used as description in rss, summary in atom
Id string // used as guid in rss, id in atom
Updated time.Time
Created time.Time
Content string
}

type Link struct {
Href, Rel string
}

type Author struct {
Name, Email string
}
32 changes: 32 additions & 0 deletions cmd/apps/apis/feed/suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
Copyright 2023 NanaFS Authors.
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 feed

import (
"testing"
"time"

. "github.com/onsi/ginkgo"
testcfg "github.com/onsi/ginkgo/config"
. "github.com/onsi/gomega"
)

func TestFeed(t *testing.T) {
RegisterFailHandler(Fail)
testcfg.DefaultReporterConfig.SlowSpecThreshold = time.Minute.Seconds()
RunSpecs(t, "Feed Suite")
}

0 comments on commit 167d876

Please sign in to comment.