@@ -2,13 +2,24 @@ package notion
22
33import (
44 "context"
5+ "fmt"
56 "github.com/jomei/notionapi"
7+ "log"
8+ "strings"
69)
710
11+ type PageUID string
12+
13+ type SigAndID struct {
14+ Signature string
15+ PageID notionapi.PageID
16+ }
17+
818type Notion struct {
919 DatabaseID notionapi.DatabaseID
1020 PageID notionapi.PageID
1121 client * notionapi.Client
22+ PageSig map [PageUID ]* SigAndID
1223}
1324
1425type Field struct {
@@ -69,6 +80,77 @@ func (r *Record) MakeProperties() notionapi.Properties {
6980 return properties
7081}
7182
83+ type PageData struct {
84+ PageID notionapi.PageID
85+ Data map [string ]string
86+ }
87+
88+ func ParsePage (page * notionapi.Page ) (PageUID , * PageData ) {
89+ var pageUID PageUID
90+ pageID := GetPageID (page .ID )
91+ data := make (map [string ]string )
92+ for name , property := range page .Properties {
93+ data [name ] = ParseProperty (property )
94+ }
95+ if v , ok := data ["_id" ]; ok {
96+ pageUID = PageUID (v )
97+ } else {
98+ log .Fatalf ("_id not found: %v" , data )
99+ }
100+ return pageUID , & PageData {PageID : notionapi .PageID (pageID ), Data : data }
101+ }
102+
103+ func ParseProperty (property notionapi.Property ) string {
104+ switch property .GetType () {
105+ case "title" :
106+ p , ok := property .(* notionapi.TitleProperty )
107+ if ! ok {
108+ log .Fatalf ("title parsed failed: %v" , property )
109+ }
110+ if len (p .Title ) == 0 {
111+ return ""
112+ }
113+ return p .Title [0 ].Text .Content
114+ case "rich_text" :
115+ p , ok := property .(* notionapi.RichTextProperty )
116+ if ! ok {
117+ log .Fatalf ("rich_text parsed failed: %v" , property )
118+ }
119+ if len (p .RichText ) == 0 {
120+ return ""
121+ }
122+ return p .RichText [0 ].Text .Content
123+ case "url" :
124+ p , ok := property .(* notionapi.URLProperty )
125+ if ! ok {
126+ log .Fatalf ("url parsed failed: %v" , property )
127+ }
128+ return p .URL
129+ case "select" :
130+ p , ok := property .(* notionapi.SelectProperty )
131+ if ! ok {
132+ log .Fatalf ("select parsed failed: %v" , property )
133+ }
134+ return p .Select .Name
135+ case "multi_select" :
136+ p , ok := property .(* notionapi.MultiSelectProperty )
137+ if ! ok {
138+ log .Fatalf ("multi_select parsed failed: %v" , property )
139+ }
140+ optionStr := ""
141+ for _ , option := range p .MultiSelect {
142+ optionStr += option .Name + ","
143+ }
144+ if len (optionStr ) > 0 {
145+ optionStr = optionStr [:len (optionStr )- 1 ]
146+ }
147+ return optionStr
148+ default :
149+ log .Fatalf ("%s not supported current" , property .GetType ())
150+ }
151+ return ""
152+ }
153+
72154func NewNotion (token string ) * Notion {
73155 client := notionapi .NewClient (notionapi .Token (token ))
74156 return & Notion {
@@ -77,11 +159,25 @@ func NewNotion(token string) *Notion {
77159}
78160
79161func (n * Notion ) WithConfig (pageID , databaseID string ) * Notion {
80- n .DatabaseID = notionapi .DatabaseID (databaseID )
81162 n .PageID = notionapi .PageID (pageID )
163+ n .DatabaseID = notionapi .DatabaseID (databaseID )
82164 return n
83165}
84166
167+ func (n * Notion ) Init () error {
168+ if n .DatabaseID != "" {
169+ records , err := n .Query ()
170+ if err != nil {
171+ return fmt .Errorf ("query failed: %v" , err )
172+ }
173+ n .PageSig = make (map [PageUID ]* SigAndID )
174+ for pageUID , pageData := range records {
175+ n .PageSig [pageUID ] = & SigAndID {PageID : pageData .PageID , Signature : GetPageSig (pageData .Data )}
176+ }
177+ }
178+ return nil
179+ }
180+
85181func (n * Notion ) Insert (record * Record ) error {
86182 ctx := context .Background ()
87183
@@ -95,3 +191,56 @@ func (n *Notion) Insert(record *Record) error {
95191 _ , err := n .client .Page .Create (ctx , pageReq )
96192 return err
97193}
194+
195+ func (n * Notion ) Update (pageID notionapi.PageID , record * Record ) error {
196+ ctx := context .Background ()
197+
198+ updateReq := & notionapi.PageUpdateRequest {
199+ Properties : record .MakeProperties (),
200+ }
201+ _ , err := n .client .Page .Update (ctx , pageID , updateReq )
202+
203+ return err
204+ }
205+
206+ func (n * Notion ) Query () (records map [PageUID ]* PageData , err error ) {
207+ ctx := context .Background ()
208+
209+ dbQueryReq := & notionapi.DatabaseQueryRequest {}
210+
211+ queryResp , err := n .client .Database .Query (ctx , n .DatabaseID , dbQueryReq )
212+
213+ if err != nil {
214+ return nil , err
215+ }
216+
217+ records = make (map [PageUID ]* PageData )
218+ for _ , result := range queryResp .Results {
219+ pageUID , pageData := ParsePage (& result )
220+ records [pageUID ] = pageData
221+ }
222+ return records , nil
223+ }
224+
225+ func (n * Notion ) InsertOrUpdate (record * Record ) error {
226+ pageUID , pageData := ParsePage (& notionapi.Page {Properties : record .MakeProperties ()})
227+
228+ if pageSig , ok := n .PageSig [pageUID ]; ok {
229+ // 签名不一致,更新行信息
230+ if pageSig .Signature != GetPageSig (pageData .Data ) {
231+ return n .Update (n .PageSig [pageUID ].PageID , record )
232+ } else {
233+ return nil
234+ }
235+ }
236+
237+ return n .Insert (record )
238+ }
239+
240+ func GetPageSig (v map [string ]string ) string {
241+ return fmt .Sprintf ("%s_%s_%s_%s_%s_%s" , v ["Difficulty" ], v ["ID" ], v ["Link" ], v ["Name" ], v ["Solved" ], v ["Tags" ])
242+ }
243+
244+ func GetPageID (objectID notionapi.ObjectID ) string {
245+ return strings .ReplaceAll (objectID .String (), "-" , "" )
246+ }
0 commit comments