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:
Alexander Navarro 2024-11-13 19:55:02 +00:00
parent 5aaacb10e3
commit b2d8dadcee
17 changed files with 963 additions and 0 deletions

View 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""}"
1 name category metadata
2 Mist web framework {"github":"rawhat/mist","package":"mist","hex":"mist"}
3 Gleeunit testing {"github":"gleam-lang/gleeunit","package":"gleeunit","hex":"gleeunit"}
4 Gleam_http http {"github":"gleam-lang/http","package":"gleam_http","hex":"gleam_http"}
5 Gleam_json serialization {"github":"gleam-lang/json","package":"gleam_json","hex":"gleam_json"}
6 Gleam_otp concurrency {"github":"gleam-lang/otp","package":"gleam_otp","hex":"gleam_otp"}

View 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""}"
1 name category metadata
2 Gin web framework {"website":"https://gin-gonic.com","github":"gin-gonic/gin","package":"github.com/gin-gonic/gin"}
3 Cobra cli {"website":"https://cobra.dev","github":"spf13/cobra","package":"github.com/spf13/cobra"}
4 GORM orm {"website":"https://gorm.io","github":"go-gorm/gorm","package":"gorm.io/gorm"}
5 Colly web scraping {"website":"http://go-colly.org","github":"gocolly/colly","package":"github.com/gocolly/colly"}
6 gorilla/websocket websockets {"github":"gorilla/websocket","package":"github.com/gorilla/websocket","protocol":"ws"}

View 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""}"
1 name category metadata
2 React ui framework {"website":"https://reactjs.org","github":"facebook/react","npm":"react","type":"library"}
3 Express web framework {"website":"https://expressjs.com","github":"expressjs/express","npm":"express","type":"framework"}
4 Axios http client {"website":"https://axios-http.com","github":"axios/axios","npm":"axios","type":"library"}
5 Socket.io websockets {"website":"https://socket.io","github":"socketio/socket.io","npm":"socket.io","protocol":"ws"}
6 Commander.js cli {"website":"https://tj.github.io/commander.js","github":"tj/commander.js","npm":"commander"}

View 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""}"
1 name category metadata
2 Laravel web framework {"website":"https://laravel.com","github":"laravel/laravel","packagist":"laravel/framework"}
3 Symfony web framework {"website":"https://symfony.com","github":"symfony/symfony","packagist":"symfony/symfony"}
4 Guzzle http client {"website":"http://guzzlephp.org","github":"guzzle/guzzle","packagist":"guzzle/guzzle"}
5 PHPUnit testing {"website":"https://phpunit.de","github":"sebastianbergmann/phpunit","packagist":"phpunit/phpunit"}
6 Composer package manager {"website":"https://getcomposer.org","github":"composer/composer","type":"tool"}

View 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""}"
1 name category metadata
2 Django web framework {"website":"https://djangoproject.com","github":"django/django","package_manager":"pip","latest_version":"4.2.0"}
3 FastAPI web framework {"website":"https://fastapi.tiangolo.com","github":"tiangolo/fastapi","package_manager":"pip","async_support":true}
4 Requests http client {"website":"https://requests.readthedocs.io","github":"psf/requests","package_manager":"pip","type":"library"}
5 Beautiful Soup web scraping {"website":"https://www.crummy.com/software/BeautifulSoup","github":"waylan/beautifulsoup","parser_support":["html","xml"]}
6 Click cli {"website":"https://click.palletsprojects.com","github":"pallets/click","type":"framework","python_versions":">=3.7"}

View 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""}"
1 name category metadata
2 Actix web framework {"website":"https://actix.rs","github":"actix/actix-web","crate":"actix-web"}
3 Clap cli {"website":"https://clap.rs","github":"clap-rs/clap","crate":"clap"}
4 Tokio async runtime {"website":"https://tokio.rs","github":"tokio-rs/tokio","crate":"tokio"}
5 Reqwest http client {"github":"seanmonstar/reqwest","crate":"reqwest","async_support":true}
6 Serde serialization {"website":"https://serde.rs","github":"serde-rs/serde","crate":"serde"}

222
examples/usage.go Normal file
View 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
}