generated from alecodes/base-template
feat: implement database handlerImplement a database api to manage nodes and relationships in a "graph databaselike" implemented in sqlite.closes #1
This commit is contained in:
parent
5aaacb10e3
commit
b2d8dadcee
17 changed files with 963 additions and 0 deletions
6
examples/mock_data/gleam.csv
Normal file
6
examples/mock_data/gleam.csv
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
name,category,metadata
|
||||
Mist,web framework,"{""github"":""rawhat/mist"",""package"":""mist"",""hex"":""mist""}"
|
||||
Gleeunit,testing,"{""github"":""gleam-lang/gleeunit"",""package"":""gleeunit"",""hex"":""gleeunit""}"
|
||||
Gleam_http,http,"{""github"":""gleam-lang/http"",""package"":""gleam_http"",""hex"":""gleam_http""}"
|
||||
Gleam_json,serialization,"{""github"":""gleam-lang/json"",""package"":""gleam_json"",""hex"":""gleam_json""}"
|
||||
Gleam_otp,concurrency,"{""github"":""gleam-lang/otp"",""package"":""gleam_otp"",""hex"":""gleam_otp""}"
|
||||
|
6
examples/mock_data/go.csv
Normal file
6
examples/mock_data/go.csv
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
name,category,metadata
|
||||
Gin,web framework,"{""website"":""https://gin-gonic.com"",""github"":""gin-gonic/gin"",""package"":""github.com/gin-gonic/gin""}"
|
||||
Cobra,cli,"{""website"":""https://cobra.dev"",""github"":""spf13/cobra"",""package"":""github.com/spf13/cobra""}"
|
||||
GORM,orm,"{""website"":""https://gorm.io"",""github"":""go-gorm/gorm"",""package"":""gorm.io/gorm""}"
|
||||
Colly,web scraping,"{""website"":""http://go-colly.org"",""github"":""gocolly/colly"",""package"":""github.com/gocolly/colly""}"
|
||||
gorilla/websocket,websockets,"{""github"":""gorilla/websocket"",""package"":""github.com/gorilla/websocket"",""protocol"":""ws""}"
|
||||
|
6
examples/mock_data/javascript.csv
Normal file
6
examples/mock_data/javascript.csv
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
name,category,metadata
|
||||
React,ui framework,"{""website"":""https://reactjs.org"",""github"":""facebook/react"",""npm"":""react"",""type"":""library""}"
|
||||
Express,web framework,"{""website"":""https://expressjs.com"",""github"":""expressjs/express"",""npm"":""express"",""type"":""framework""}"
|
||||
Axios,http client,"{""website"":""https://axios-http.com"",""github"":""axios/axios"",""npm"":""axios"",""type"":""library""}"
|
||||
Socket.io,websockets,"{""website"":""https://socket.io"",""github"":""socketio/socket.io"",""npm"":""socket.io"",""protocol"":""ws""}"
|
||||
Commander.js,cli,"{""website"":""https://tj.github.io/commander.js"",""github"":""tj/commander.js"",""npm"":""commander""}"
|
||||
|
6
examples/mock_data/php.csv
Normal file
6
examples/mock_data/php.csv
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
name,category,metadata
|
||||
Laravel,web framework,"{""website"":""https://laravel.com"",""github"":""laravel/laravel"",""packagist"":""laravel/framework""}"
|
||||
Symfony,web framework,"{""website"":""https://symfony.com"",""github"":""symfony/symfony"",""packagist"":""symfony/symfony""}"
|
||||
Guzzle,http client,"{""website"":""http://guzzlephp.org"",""github"":""guzzle/guzzle"",""packagist"":""guzzle/guzzle""}"
|
||||
PHPUnit,testing,"{""website"":""https://phpunit.de"",""github"":""sebastianbergmann/phpunit"",""packagist"":""phpunit/phpunit""}"
|
||||
Composer,package manager,"{""website"":""https://getcomposer.org"",""github"":""composer/composer"",""type"":""tool""}"
|
||||
|
6
examples/mock_data/python.csv
Normal file
6
examples/mock_data/python.csv
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
name,category,metadata
|
||||
Django,web framework,"{""website"":""https://djangoproject.com"",""github"":""django/django"",""package_manager"":""pip"",""latest_version"":""4.2.0""}"
|
||||
FastAPI,web framework,"{""website"":""https://fastapi.tiangolo.com"",""github"":""tiangolo/fastapi"",""package_manager"":""pip"",""async_support"":true}"
|
||||
Requests,http client,"{""website"":""https://requests.readthedocs.io"",""github"":""psf/requests"",""package_manager"":""pip"",""type"":""library""}"
|
||||
Beautiful Soup,web scraping,"{""website"":""https://www.crummy.com/software/BeautifulSoup"",""github"":""waylan/beautifulsoup"",""parser_support"":[""html"",""xml""]}"
|
||||
Click,cli,"{""website"":""https://click.palletsprojects.com"",""github"":""pallets/click"",""type"":""framework"",""python_versions"":"">=3.7""}"
|
||||
|
6
examples/mock_data/rust.csv
Normal file
6
examples/mock_data/rust.csv
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
name,category,metadata
|
||||
Actix,web framework,"{""website"":""https://actix.rs"",""github"":""actix/actix-web"",""crate"":""actix-web""}"
|
||||
Clap,cli,"{""website"":""https://clap.rs"",""github"":""clap-rs/clap"",""crate"":""clap""}"
|
||||
Tokio,async runtime,"{""website"":""https://tokio.rs"",""github"":""tokio-rs/tokio"",""crate"":""tokio""}"
|
||||
Reqwest,http client,"{""github"":""seanmonstar/reqwest"",""crate"":""reqwest"",""async_support"":true}"
|
||||
Serde,serialization,"{""website"":""https://serde.rs"",""github"":""serde-rs/serde"",""crate"":""serde""}"
|
||||
|
222
examples/usage.go
Normal file
222
examples/usage.go
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/csv"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
synchronizator "git.alecodes.page/alecodes/synchronizator/pkg"
|
||||
_ "modernc.org/sqlite"
|
||||
)
|
||||
|
||||
type ProgrammingLanguage struct {
|
||||
Name string
|
||||
}
|
||||
|
||||
func (language *ProgrammingLanguage) ToNode() (string, string, []byte, error) {
|
||||
metadata, err := json.Marshal("{\"test\": \"foo\"}")
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
return "PROGRAMMING_LANGUAGE", language.Name, metadata, nil
|
||||
}
|
||||
|
||||
func (language *ProgrammingLanguage) FromNode(_class string, name string, metadata []byte) error {
|
||||
if _class != "PROGRAMMING_LANGUAGE" {
|
||||
return fmt.Errorf("invalid class %s", _class)
|
||||
}
|
||||
language.Name = name
|
||||
return nil
|
||||
}
|
||||
|
||||
type Library struct {
|
||||
Name string `json:"name"`
|
||||
Category string `json:"category"`
|
||||
Metadata map[string]interface{} `json:"metadata"`
|
||||
}
|
||||
|
||||
func (library *Library) ToNode() (string, string, []byte, error) {
|
||||
metadata, err := json.Marshal(library.Metadata)
|
||||
if err != nil {
|
||||
return "", "", nil, err
|
||||
}
|
||||
return "LIBRARY", library.Name, metadata, nil
|
||||
}
|
||||
|
||||
func (library *Library) FromNode(_class string, name string, metadata []byte) error {
|
||||
if _class != "LIBRARY" {
|
||||
return fmt.Errorf("invalid class %s", _class)
|
||||
}
|
||||
if err := json.Unmarshal(metadata, &library.Metadata); err != nil {
|
||||
return err
|
||||
}
|
||||
library.Name = name
|
||||
return nil
|
||||
}
|
||||
|
||||
type (
|
||||
BelognsTo struct{}
|
||||
IsSame struct{}
|
||||
)
|
||||
|
||||
func main() {
|
||||
connection, err := sql.Open("sqlite", "db.sql")
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
defer connection.Close()
|
||||
|
||||
opts := synchronizator.DefaultOptions
|
||||
// opts.Log_level = synchronizator.DEBUG
|
||||
opts.DANGEROUSLY_DROP_TABLES = true
|
||||
|
||||
sync, err := synchronizator.New(connection, opts)
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
languages, err := loadData()
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
}
|
||||
|
||||
for language, libraries := range languages {
|
||||
_, err := generateCollection(
|
||||
&ProgrammingLanguage{Name: strings.ToUpper(language)},
|
||||
libraries,
|
||||
sync,
|
||||
)
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
|
||||
// fmt.Fprintf(
|
||||
// os.Stderr,
|
||||
// "libraries_collection%+v\n",
|
||||
// libraries_collection,
|
||||
// )
|
||||
}
|
||||
|
||||
golang, err := sync.GetNode(1)
|
||||
if err != nil {
|
||||
println(err)
|
||||
}
|
||||
fmt.Println("%v", golang)
|
||||
relationships, err := golang.GetOutRelations()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, relationship := range relationships {
|
||||
fmt.Printf("%v -> %v -> %v\n", relationship.From, relationship.GetClass(), relationship.To)
|
||||
}
|
||||
}
|
||||
|
||||
// generateCollection Main example of the usage of the synchronizator package
|
||||
func generateCollection(
|
||||
language *ProgrammingLanguage,
|
||||
libraries []Library,
|
||||
sync *synchronizator.DB,
|
||||
) (*synchronizator.Collection, error) {
|
||||
language_libraries, err := sync.NewCollection(language)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, library := range libraries {
|
||||
node, err := sync.NewNode(&library)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := &Library{}
|
||||
if err := node.Unmarshall(data); err != nil {
|
||||
println(err)
|
||||
}
|
||||
|
||||
if err := language_libraries.AddChild(node); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
return language_libraries, nil
|
||||
}
|
||||
|
||||
func loadData() (map[string][]Library, error) {
|
||||
// Find all CSV files
|
||||
files, err := filepath.Glob("examples/mock_data/*.csv")
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to glob files: %w", err)
|
||||
}
|
||||
|
||||
result := make(map[string][]Library)
|
||||
|
||||
for _, file := range files {
|
||||
// Load CSV file
|
||||
libraries, err := processCSVFile(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to process %s: %w", file, err)
|
||||
}
|
||||
|
||||
// Use base filename without extension as language_name
|
||||
language_name := filepath.Base(file)
|
||||
language_name = language_name[:len(language_name)-len(filepath.Ext(language_name))]
|
||||
|
||||
result[language_name] = libraries
|
||||
}
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func processCSVFile(filename string) ([]Library, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
reader := csv.NewReader(file)
|
||||
|
||||
// Skip header
|
||||
_, err = reader.Read()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var libraries []Library
|
||||
|
||||
// Read records
|
||||
for {
|
||||
record, err := reader.Read()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Parse metadata JSON
|
||||
var metadata map[string]interface{}
|
||||
if err := json.Unmarshal([]byte(record[2]), &metadata); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse metadata: %w", err)
|
||||
}
|
||||
|
||||
library := Library{
|
||||
Name: record[0],
|
||||
Category: record[1],
|
||||
Metadata: metadata,
|
||||
}
|
||||
libraries = append(libraries, library)
|
||||
}
|
||||
|
||||
return libraries, nil
|
||||
}
|
||||
Reference in a new issue