appserver

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 28, 2023 License: MIT Imports: 20 Imported by: 0

README

Shopware


Foundation for Shopware apps based on Go

This library provides helper functions to write Shopware apps with a Go backend server. The library handles authorization as well as webhooks and actions.

Menu

Features

  • Automated handshake for easy installation in Shopware
  • Easy configuration with no additional router set-up needed
  • Generic endpoints for admin action buttons and webhooks
  • Written in Go, a language with high memory safety guarantees

Quick start

Installing

To start using the app server, install Go and run go get:

$ go get github.com/janbuecker/shopware-appserver-go

Storage engines

The app server comes with one storage engine included: in-memory.

This storage resets on every restart of the server and should only be used for quick-start purposes. All information is lost when the process is killed. This storage is used by default.

Events

First, register a POST route in your web server and use HandleWebhook inside the handler:

mux.HandleFunc("/webhook", func(w http.ResponseWriter, r *http.Request) {
    if err := srv.HandleWebhook(r); err != nil {
      // handle errors
   }

    // webhook handled successfully
})

To listen on an event, add the event to your manifest.xml file in your app and point it to your webhook endpoint. Verifying the signature is done automatically for you.

manifest.xml

<webhooks>
    <webhook name="orderCompleted" url="https://appserver.com/webhooks" event="checkout.order.placed"/>
</webhooks>

<permissions>
   <read>order</read>
</permissions>

You can then register an event listener to the app server:

srv.Event("checkout.order.placed", func(ctx context.Context, webhook appserver.WebhookRequest, api *appserver.APIClient) error {
    // do something on this event

    return nil
})

Action buttons

First, register a POST route in your web server and use HandleAction inside the handler:

mux.HandleFunc("/actions", func(w http.ResponseWriter, r *http.Request) {
    if err := srv.HandleAction(r); err != nil {
      // handle errors
   }

    // action handled successfully
})

To listen on a click on an action button, add the action button to your manifest.xml file in your app and point it to your actions endpoint. Verifying the signature is done automatically for you.

manifest.xml

<admin>
    <action-button action="doSomething" entity="product" view="detail" url="https://appserver.com/actions">
        <label>do something</label>
    </action-button>
</admin>

You can then register an admin action listener to the app server:

srv.Action("product", "doSomething", func(ctx context.Context, action appserver.ActionRequest, api *appserver.APIClient) error {
    // do something when someone clicks the action button

    return nil
})

Full example

Here is a full example on an app server, that uses the standard http package and listens for events and action buttons.

package main

import (
   "encoding/json"
   "log"
   "net/http"
   
   appserver "github.com/janbuecker/shopware-appserver-go"
)

func main() {
   srv := appserver.NewServer(
      "AppName",
      "AppSecret",
      "https://appserver.com/setup/register-confirm",
   )

   // event listener
   srv.Event("checkout.order.placed", func(ctx context.Context, webhook appserver.WebhookRequest, api *appserver.APIClient) error {
      // do something on this event
      
      return nil
   })

   // action buttons
   srv.Action("product", "doSomething", func(ctx context.Context, action appserver.ActionRequest, api *appserver.APIClient) error {
      // do something when someone clicks the action button
      
      return nil
   })
   
   // register routes and start server
   mux := http.NewServeMux()
   mux.HandleFunc("/webhooks", func(w http.ResponseWriter, r *http.Request) {
      if err := srv.HandleWebhook(r); err != nil {
         http.Error(w, err.Error(), http.StatusBadRequest)
         return
      }

      w.WriteHeader(200)
   })
   mux.HandleFunc("/actions", func(w http.ResponseWriter, r *http.Request) {
      if err := srv.HandleAction(r); err != nil {
         http.Error(w, err.Error(), http.StatusBadRequest)
         return
      }

      w.WriteHeader(200)
   })
   mux.HandleFunc("/setup/register", func(w http.ResponseWriter, r *http.Request) {
      reg, err := srv.HandleRegistration(r)
      if err != nil {
         http.Error(w, err.Error(), http.StatusBadRequest)
         return
      }

      regJSON, err := json.Marshal(reg)
      if err != nil {
         http.Error(w, err.Error(), http.StatusInternalServerError)
         return
      }

      w.WriteHeader(200)
      w.Write(regJSON)
   })

   mux.HandleFunc("/setup/register-confirm", func(w http.ResponseWriter, r *http.Request) {
      if err := srv.HandleConfirm(r); err != nil {
         http.Error(w, err.Error(), http.StatusBadRequest)
         return
      }

      w.WriteHeader(200)
   })

   log.Println("Listening on port 10100")
   log.Fatal(http.ListenAndServe(":10100", mux))
}

About

The app server written in Go is an open-source project and maintained by @janbuecker.

Documentation

Index

Constants

View Source
const (
	TotalCountModeDefault  = 0
	TotalCountModeExact    = 1
	TotalCountModeNextPage = 2

	SearchFilterTypeEquals    = "equals"
	SearchFilterTypeEqualsAny = "equalsAny"

	SearchSortDirectionAscending  = "ASC"
	SearchSortDirectionDescending = "DESC"
)
View Source
const (
	AppSignatureKey  = "shopware-app-signature"
	ShopSignatureKey = "shopware-shop-signature"
)

Variables

View Source
var ErrActionMissingAction = errors.New("missing action or entity")
View Source
var ErrCredentialsNotFound = errors.New("credentials for shop not found")
View Source
var ErrWebhookMissingEvent = errors.New("missing event")

Functions

This section is empty.

Types

type APIClient

type APIClient struct {
	// contains filtered or unexported fields
}

func (*APIClient) GetAppConfig

func (c *APIClient) GetAppConfig(ctx context.Context) (map[string]interface{}, error)

func (*APIClient) Request

func (c *APIClient) Request(ctx context.Context, method string, path string, payload interface{}) (*http.Response, error)

type ActionHandler

type ActionHandler func(ctx context.Context, action ActionRequest, api *APIClient) error

type ActionHandlerNotFoundError

type ActionHandlerNotFoundError struct {
	// contains filtered or unexported fields
}

func (ActionHandlerNotFoundError) Error

type ActionRequest

type ActionRequest struct {
	*AppRequest

	Data struct {
		IDs    []string `json:"ids"`
		Entity string   `json:"entity"`
		Action string   `json:"action"`
	} `json:"data"`

	Meta struct {
		Timestamp   int64  `json:"timestamp"`
		ReferenceID string `json:"reference"`
		LanguageID  string `json:"language"`
	} `json:"meta"`
}

type AppRequest

type AppRequest struct {
	Source Source `json:"source"`
}

type CredentialStore

type CredentialStore interface {
	Store(ctx context.Context, credentials Credentials) error
	Get(ctx context.Context, shopID string) (Credentials, error)
	Delete(ctx context.Context, shopID string) error
}

type Credentials

type Credentials struct {
	APIKey     string `json:"apiKey"`
	SecretKey  string `json:"secretKey"`
	Timestamp  string `json:"timestamp" query:"timestamp"`
	ShopURL    string `json:"shopUrl" query:"shop-url"`
	ShopID     string `json:"shopId" query:"shop-id"`
	ShopSecret string `json:"shopSecret"`
}

type MemoryCredentialStore

type MemoryCredentialStore struct {
	// contains filtered or unexported fields
}

func NewMemoryCredentialStore

func NewMemoryCredentialStore() *MemoryCredentialStore

func (*MemoryCredentialStore) Delete

func (m *MemoryCredentialStore) Delete(ctx context.Context, shopID string) error

func (*MemoryCredentialStore) Get

func (*MemoryCredentialStore) Store

func (m *MemoryCredentialStore) Store(ctx context.Context, credentials Credentials) error

type RegistrationResponse

type RegistrationResponse struct {
	Proof           string `json:"proof"`
	Secret          string `json:"secret"`
	ConfirmationURL string `json:"confirmation_url"`
}
type Search struct {
	Includes       map[string][]string `json:"includes,omitempty"`
	Page           int64               `json:"page,omitempty"`
	Limit          int64               `json:"limit,omitempty"`
	IDs            []string            `json:"ids,omitempty"`
	Filter         []SearchFilter      `json:"filter,omitempty"`
	PostFilter     []SearchFilter      `json:"postFilter,omitempty"`
	Sort           []SearchSort        `json:"sort,omitempty"`
	Term           string              `json:"term,omitempty"`
	TotalCountMode int                 `json:"totalCountMode,omitempty"`
}

type SearchFilter

type SearchFilter struct {
	Type  string      `json:"type"`
	Field string      `json:"field"`
	Value interface{} `json:"value"`
}

type SearchResponse

type SearchResponse struct {
	Total        int64       `json:"total"`
	Data         interface{} `json:"data"`
	Aggregations interface{} `json:"aggregations"`
}

type SearchSort

type SearchSort struct {
	Direction      string `json:"order"`
	Field          string `json:"field"`
	NaturalSorting bool   `json:"naturalSorting"`
}

type Server

type Server struct {
	// contains filtered or unexported fields
}

func NewServer

func NewServer(appName string, appSecret string, confirmationURL string, opts ...ServerOpt) *Server

func (*Server) Action

func (srv *Server) Action(entity string, action string, handler ActionHandler)

func (*Server) Event

func (srv *Server) Event(event string, handler WebhookHandler)

func (*Server) HandleAction

func (srv *Server) HandleAction(req *http.Request) error

func (*Server) HandleConfirm

func (srv *Server) HandleConfirm(req *http.Request) error

func (*Server) HandleRegistration

func (srv *Server) HandleRegistration(req *http.Request) (RegistrationResponse, error)

func (*Server) HandleWebhook

func (srv *Server) HandleWebhook(req *http.Request) error

func (*Server) VerifyPageSignature added in v0.1.0

func (srv *Server) VerifyPageSignature(req *http.Request) error

type ServerOpt

type ServerOpt func(s *Server)

func WithCredentialStore

func WithCredentialStore(store CredentialStore) ServerOpt

func WithHTTPClient

func WithHTTPClient(client *http.Client) ServerOpt

type SignatureVerificationError

type SignatureVerificationError struct {
	// contains filtered or unexported fields
}

func (SignatureVerificationError) Error

func (SignatureVerificationError) Unwrap

func (e SignatureVerificationError) Unwrap() error

type Source

type Source struct {
	ShopID     string `json:"shopId"`
	ShopURL    string `json:"url"`
	AppVersion string `json:"appVersion"`
}

type WebhookHandler

type WebhookHandler func(ctx context.Context, webhook WebhookRequest, api *APIClient) error

type WebhookHandlerNotFoundError

type WebhookHandlerNotFoundError struct {
	// contains filtered or unexported fields
}

func (WebhookHandlerNotFoundError) Error

type WebhookRequest

type WebhookRequest struct {
	*AppRequest

	Data struct {
		Payload map[string]interface{} `json:"payload"`
		Event   string                 `json:"event"`
	} `json:"data"`
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL