...

Source file src/github.com/cybertec-postgresql/pgwatch/v3/internal/webserver/source.go

Documentation: github.com/cybertec-postgresql/pgwatch/v3/internal/webserver

     1  package webserver
     2  
     3  import (
     4  	"errors"
     5  	"io"
     6  	"net/http"
     7  
     8  	jsoniter "github.com/json-iterator/go"
     9  
    10  	"github.com/cybertec-postgresql/pgwatch/v3/internal/sources"
    11  )
    12  
    13  func (server *WebUIServer) handleSources(w http.ResponseWriter, r *http.Request) {
    14  	var (
    15  		err    error
    16  		status = http.StatusInternalServerError
    17  		params []byte
    18  		res    string
    19  	)
    20  
    21  	defer func() {
    22  		if err != nil {
    23  			http.Error(w, err.Error(), status)
    24  		}
    25  	}()
    26  
    27  	switch r.Method {
    28  	case http.MethodGet:
    29  		// return monitored databases
    30  		if res, err = server.GetSources(); err != nil {
    31  			return
    32  		}
    33  		_, err = w.Write([]byte(res))
    34  
    35  	case http.MethodPost:
    36  		// add new monitored database (REST-compliant: POST for creation only)
    37  		if params, err = io.ReadAll(r.Body); err != nil {
    38  			return
    39  		}
    40  		err = server.CreateSource(params)
    41  		if err != nil {
    42  			if errors.Is(err, sources.ErrSourceExists) {
    43  				status = http.StatusConflict
    44  			}
    45  			return
    46  		}
    47  		w.WriteHeader(http.StatusCreated)
    48  
    49  	case http.MethodOptions:
    50  		w.Header().Set("Allow", "GET, POST, OPTIONS")
    51  		w.WriteHeader(http.StatusOK)
    52  
    53  	default:
    54  		w.Header().Set("Allow", "GET, POST, OPTIONS")
    55  		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
    56  	}
    57  }
    58  
    59  // GetSources returns the list of sources fo find databases for monitoring
    60  func (server *WebUIServer) GetSources() (res string, err error) {
    61  	var dbs sources.Sources
    62  	if dbs, err = server.sourcesReaderWriter.GetSources(); err != nil {
    63  		return
    64  	}
    65  	b, _ := jsoniter.ConfigFastest.Marshal(dbs)
    66  	res = string(b)
    67  	return
    68  }
    69  
    70  // DeleteSource removes the source from the list of configured sources
    71  func (server *WebUIServer) DeleteSource(database string) error {
    72  	return server.sourcesReaderWriter.DeleteSource(database)
    73  }
    74  
    75  // UpdateSource updates the configured source information
    76  func (server *WebUIServer) UpdateSource(params []byte) error {
    77  	var md sources.Source
    78  	err := jsoniter.ConfigFastest.Unmarshal(params, &md)
    79  	if err != nil {
    80  		return err
    81  	}
    82  	return server.sourcesReaderWriter.UpdateSource(md)
    83  }
    84  
    85  // CreateSource creates a new source (for REST collection endpoint)
    86  func (server *WebUIServer) CreateSource(params []byte) error {
    87  	var md sources.Source
    88  	err := jsoniter.ConfigFastest.Unmarshal(params, &md)
    89  	if err != nil {
    90  		return err
    91  	}
    92  	return server.sourcesReaderWriter.CreateSource(md)
    93  }
    94  
    95  // handleSourceItem handles individual source operations using REST-compliant HTTP methods
    96  // and path parameters like /source/{name}
    97  func (server *WebUIServer) handleSourceItem(w http.ResponseWriter, r *http.Request) {
    98  	name := r.PathValue("name")
    99  	if name == "" {
   100  		http.Error(w, "source name is required", http.StatusBadRequest)
   101  		return
   102  	}
   103  
   104  	switch r.Method {
   105  	case http.MethodGet:
   106  		server.getSourceByName(w, name)
   107  	case http.MethodPut:
   108  		server.updateSourceByName(w, r, name)
   109  	case http.MethodDelete:
   110  		server.deleteSourceByName(w, name)
   111  	case http.MethodOptions:
   112  		w.Header().Set("Allow", "GET, PUT, DELETE, OPTIONS")
   113  		w.WriteHeader(http.StatusOK)
   114  	default:
   115  		w.Header().Set("Allow", "GET, PUT, DELETE, OPTIONS")
   116  		http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
   117  	}
   118  }
   119  
   120  // getSourceByName returns a specific source by name
   121  func (server *WebUIServer) getSourceByName(w http.ResponseWriter, name string) {
   122  	var (
   123  		err    error
   124  		status = http.StatusInternalServerError
   125  		srcs   sources.Sources
   126  	)
   127  
   128  	defer func() {
   129  		if err != nil {
   130  			http.Error(w, err.Error(), status)
   131  		}
   132  	}()
   133  
   134  	if srcs, err = server.sourcesReaderWriter.GetSources(); err != nil {
   135  		return
   136  	}
   137  
   138  	for _, src := range srcs {
   139  		if src.Name == name {
   140  			b, _ := jsoniter.ConfigFastest.Marshal(src)
   141  			w.Header().Set("Content-Type", "application/json")
   142  			_, err = w.Write(b)
   143  			return
   144  		}
   145  	}
   146  
   147  	err = sources.ErrSourceNotFound
   148  	status = http.StatusNotFound
   149  }
   150  
   151  // updateSourceByName updates an existing source using PUT semantics
   152  func (server *WebUIServer) updateSourceByName(w http.ResponseWriter, r *http.Request, name string) {
   153  	params, err := io.ReadAll(r.Body)
   154  	if err != nil {
   155  		http.Error(w, "invalid request body", http.StatusBadRequest)
   156  		return
   157  	}
   158  
   159  	var md sources.Source
   160  	if err := jsoniter.ConfigFastest.Unmarshal(params, &md); err != nil {
   161  		http.Error(w, "invalid JSON format", http.StatusBadRequest)
   162  		return
   163  	}
   164  
   165  	// Name in body must match the URL parameter
   166  	if md.Name != name {
   167  		http.Error(w, "name in URL and body must match", http.StatusBadRequest)
   168  		return
   169  	}
   170  
   171  	if err := server.sourcesReaderWriter.UpdateSource(md); err != nil {
   172  		http.Error(w, err.Error(), http.StatusInternalServerError)
   173  		return
   174  	}
   175  
   176  	w.WriteHeader(http.StatusOK)
   177  }
   178  
   179  // deleteSourceByName deletes a source by name
   180  func (server *WebUIServer) deleteSourceByName(w http.ResponseWriter, name string) {
   181  	if err := server.sourcesReaderWriter.DeleteSource(name); err != nil {
   182  		http.Error(w, err.Error(), http.StatusInternalServerError)
   183  		return
   184  	}
   185  
   186  	w.WriteHeader(http.StatusOK)
   187  }
   188