iT邦幫忙

2023 iThome 鐵人賽

DAY 22
0
SideProject30

行事曆不再NG:Notion API&Google Calendar跨平台整合發想系列 第 22

Day 22 Notion API & Google Calendar API Integration - Create Notion Database

  • 分享至 

  • xImage
  •  

現在就一隻一隻API在做,這邊就順便紀錄

今天處理的事Create Notion Database,這隻之前已經有講過了,但這次把內容修改了一下

先上API spec,會盡量照著這邊的內容來改code

/notion/createNotionDatabase/{pageId}:
    post:
      summary: Create a Notion database
      parameters:
        - name: pageId
          in: path
          required: true
          description: ID of the Notion page to create the database under
          schema:
            type: string
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: array
                  items:
                    type: string
                properties:
                  type: object
                  description: JSON object defining database properties
      responses:
        '201':
          description: Notion database created successfully
        '400':
          description: Invalid input

Secret

首先是secret,之前取得secret的方法是這樣

controller/notion.go

func (c *Controller) CreateNotionDatabase(ctx *gin.Context) {
	viper.AddConfigPath("./config") // config所在的目錄路徑
	viper.SetConfigName("secret")

	err := viper.ReadInConfig()
	if err != nil {
		log.Fatalln(err)
	}
	auth := viper.Get("Authorization")
}

因為這個function會很多地方都用到,所以寫成一個common的function

util/configHandler.go

package util

import (
	"github.com/spf13/viper"
)

type ConfigHandler struct {
}

func NewConfigHandler() *ConfigHandler {
	return &ConfigHandler{}
}

func (c *ConfigHandler) GetSecretConfig() *viper.Viper {
	viper.AddConfigPath("./config") // config所在的目錄路徑
	viper.SetConfigName("secret")

	err := viper.ReadInConfig()
	if err != nil {
		panic(err)
	}

	return viper.GetViper()
}

這樣寫的話只要在code中new一個新的物件就可以取得secret了,所以原本的code會改成

controller/notion.go

func (c *Controller) CreateNotionDatabase(ctx *gin.Context) {
	// Get Authorization from config
	configHandler := util.NewConfigHandler()
	auth := configHandler.GetSecretConfig().Get("Authorization")
	...
}

Request & Router

接著要處理這隻api的request,這邊的request照上面的內容來看的話,在path的地方會需要一個pageId,在body的地方會需要title(array)跟properties(json)

這邊就需要創建一個model,來存這兩個body的資訊

model/requestModel/notionRequest.go

package requestModel

type NotionCreateDatabaseRequest struct {
	Title      []map[string]interface{} `json:"title"`
	Properties map[string]interface{}   `json:"properties"`
}

用這樣的結構就可以傳入這兩個參數到api中了

接著要來設定swagger的內容

controller/notion.go

// CreateNotionDatabase godoc
//
//	@Summary		Create a new Notion Database
//	@Description	Creates a database as a subpage in the specified parent page, with the specified properties schema. Currently, the parent of a new database must be a Notion page or a wiki database.
//	@Tags			notion
//	@Accept		json
//	@Produce		json
//	@Param			pageId	path	string	true	"Page ID"
//	@Param 		request body 	requestModel.NotionCreateDatabaseRequest true	"Request Body"
//	@Success		200		{array}		responseModel.NotionCreateDatabaseResponse
//	@Failure		400		{string}	string			"Invalid input"
//	@Router		/api/v1/notion/createDatabase/{pageId} [post]
func (c *Controller) CreateNotionDatabase(ctx *gin.Context) {
	...

這邊主要有加上幾個東西,Param試用來設定要傳入的參數,這邊來解釋一些內容

  • pageId: notion的page id
  • path: param的型態
  • string: 資料型態
  • true: 是不是required
  • “Page ID”: swagger上面顯示的名稱

另外一個則是傳入的資料型態為requestModel.NotionCreateDatabaseRequest,param的型態為body

還有一個改變的部分就是Router的部分,這邊最後面加上/{pageId},就是要讓這個參數傳進來

接下來來改一下main.go裡面的router

main.go

func main() {
	r := gin.Default()

	c := controller.NewController()

	v1 := r.Group("/api/v1")
	{
		notion := v1.Group("/notion")
		{
			notion.POST("/createDatabase/:pageId", c.CreateNotionDatabase)
		}
	}
	...
}

這邊最後面加上/:pageId,就會接到這個參數了

Controller

接著才要講Controller的內容,code的部分一點一點上

func (c *Controller) CreateNotionDatabase(ctx *gin.Context) {
	// Get Authorization from config
	configHandler := util.NewConfigHandler()
	auth := configHandler.GetSecretConfig().Get("Authorization")

	pageId := ctx.Param("pageId")
	// Bind the request body to struct
	var requests requestModel.NotionCreateDatabaseRequest
	if err := ctx.ShouldBindJSON(&requests); err != nil {
		ctx.JSON(400, gin.H{"error": err.Error()})
		return
	}
	...
}

上面接到secret後,下面這邊是要處理傳入的參數

ctx.Param("pageId")可以取得pageId

然後用ShouldBindJSON(&requests)可以把body的內容傳進去requests中,並且這邊順便做一個error handling

接著裡面的兩塊內容(title, properties)要轉成json

 func (c *Controller) CreateNotionDatabase(ctx *gin.Context) {
	...
	// Marshal the struct to json
	titleJson, err := json.Marshal(requests.Title)
	if err != nil {
		log.Fatalln(err)
	}
	propertiesJson, err := json.Marshal(requests.Properties)
	if err != nil {
		log.Fatalln(err)
	}
	...
}

再來要小改一下之前的body內容,把這兩個json傳入

 func (c *Controller) CreateNotionDatabase(ctx *gin.Context) {
	...
	// Send the request to Notion API
	client := handler.NewClient()
	header := map[string]string{
		"Authorization":  auth.(string),
		"Notion-Version": "2022-06-28",
		"Content-Type":   "application/json",
	}
	bodyString := `{
		"is_inline": true,
		"parent": {
			"type": "page_id",
			"page_id": "` + pageId + `"
		},
		"title": ` + string(titleJson) + `,
		"properties": ` + string(propertiesJson) + ` 
	}`
	body := []byte(bodyString)
	...
}

所以把現在body的內容刪減到只剩is_inline、parent、title、properties這幾個內容

剩下的地方就是把接response的地方改一下就好了

這邊上一下完整的code

// CreateNotionDatabase godoc
//
//	@Summary		Create a new Notion Database
//	@Description	Creates a database as a subpage in the specified parent page, with the specified properties schema. Currently, the parent of a new database must be a Notion page or a wiki database.
//	@Tags			notion
//	@Accept			json
//	@Produce		json
//	@Param			pageId	path	string	true	"Page ID"
//	@Param 			request body 	requestModel.NotionCreateDatabaseRequest true	"Request Body"
//	@Success		200		{array}		responseModel.NotionCreateDatabaseResponse
//	@Failure		400		{string}	string			"Invalid input"
//	@Router			/api/v1/notion/createDatabase/{pageId} [post]
func (c *Controller) CreateNotionDatabase(ctx *gin.Context) {
	// Get Authorization from config
	configHandler := util.NewConfigHandler()
	auth := configHandler.GetSecretConfig().Get("Authorization")

	pageId := ctx.Param("pageId")
	// Bind the request body to struct
	var requests requestModel.NotionCreateDatabaseRequest
	if err := ctx.ShouldBindJSON(&requests); err != nil {
		ctx.JSON(400, gin.H{"error": err.Error()})
		return
	}

	// Marshal the struct to json
	titleJson, err := json.Marshal(requests.Title)
	if err != nil {
		log.Fatalln(err)
	}
	propertiesJson, err := json.Marshal(requests.Properties)
	if err != nil {
		log.Fatalln(err)
	}

	// Send the request to Notion API
	client := handler.NewClient()
	header := map[string]string{
		"Authorization":  auth.(string),
		"Notion-Version": "2022-06-28",
		"Content-Type":   "application/json",
	}
	bodyString := `{
		"is_inline": true,
		"parent": {
			"type": "page_id",
			"page_id": "` + pageId + `"
		},
		"title": ` + string(titleJson) + `,
		"properties": ` + string(propertiesJson) + ` 
	}`
	body := []byte(bodyString)

	response, err := client.Post("https://api.notion.com/v1/databases", header, body)
	if err != nil {
		log.Fatalln(err)
	}
	defer response.Body.Close()

	// Change the response body to []byte type
	responseBody, err := io.ReadAll(response.Body)
	if err != nil {
		log.Fatalln(err)
	}
	bodyStr := string(responseBody)
	var data []byte = []byte(bodyStr)

	// Unmarshal the response body to struct
	var responseCreateNotionDatabase responseModel.NotionCreateDatabaseResponse
	json.Unmarshal(data, &responseCreateNotionDatabase)

	ctx.JSON(http.StatusOK, responseCreateNotionDatabase)
}

Response

其實不用動太多,只要改一下資料結構就好了

model/responseModel/notionResponse.go

package responseModel

type NotionCreateDatabaseResponse struct {
	Object         string                   `json:"object"`
	ID             string                   `json:"id"`
	CreatedTime    string                   `json:"created_time"`
	LastEditedTime string                   `json:"last_edited_time"`
	URL            string                   `json:"url"`
	Title          []map[string]interface{} `json:"title"`
	Properties     map[string]interface{}   `json:"properties"`
	Parent         struct {
		Type   string `json:"type"`
		PageID string `json:"page_id"`
	} `json:"parent"`
	Archived bool `json:"archived"`
	IsInline bool `json:"is_inline"`
}

這隻之前叫做model/notion.go,因為這邊是要處理response的內容所以就換了一個資料夾

將這些內容改好後,用bash update-swagger.sh更新一下swagger的內容

Demo

swagger的畫面現在會長這樣

https://ithelp.ithome.com.tw/upload/images/20231007/201408693LRw78yZP0.png

PageId在Day 7的時候有講過,可以去那邊看

在來給一下這邊Request Body的內容

{
  "properties":{
    "Title":{
      "id":"title",
      "name":"Title",
      "type":"title",
      "title":{
        
      }
    },
    "Date":{
      "id":"date",
      "name":"DateTime",
      "type":"date",
      "date":{
        
      }
    }
  },
  "title":[
    {
      "type":"text",
      "text":{
        "content":"Google Calendar Sync",
        "link":null
      }
    }
  ]
}

昨天有講到這個db現在只需要兩個欄位,一個叫Title,另一個叫做DateTime

將這些資訊都打上之後,執行後會在Notion上面看到這樣

https://ithelp.ithome.com.tw/upload/images/20231007/20140869qM9Nj2XPNc.png

然後這邊是Response接回來的內容

{
  "object": "database",
  "id": "",
  "created_time": "2023-10-07T14:49:00.000Z",
  "last_edited_time": "2023-10-07T14:49:00.000Z",
  "url": "",
  "title": [
    {
      "annotations": {
        "bold": false,
        "code": false,
        "color": "default",
        "italic": false,
        "strikethrough": false,
        "underline": false
      },
      "href": null,
      "plain_text": "Google Calendar Sync",
      "text": {
        "content": "Google Calendar Sync",
        "link": null
      },
      "type": "text"
    }
  ],
  "properties": {
    "Date": {
      "date": {},
      "id": "s%7D%5CP",
      "name": "Date",
      "type": "date"
    },
    "Title": {
      "id": "title",
      "name": "Title",
      "title": {},
      "type": "title"
    }
  },
  "parent": {
    "type": "page_id",
    "page_id": ""
  },
  "archived": false,
  "is_inline": true
}

明天再繼續用下一隻API


上一篇
Day 21 Notion API & Google Calendar API Integration
下一篇
Day 23 Notion API & Google Calendar API Integration - Query Notion Database
系列文
行事曆不再NG:Notion API&Google Calendar跨平台整合發想30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言