Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update to use new Modus manifest #462

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ In previous releases, the name "Hypermode" was used for all three._
- Update command-line args and env variables [#459](https://github.com/hypermodeinc/modus/pull/459)
- Update Sentry telemetry collection rules [#460](https://github.com/hypermodeinc/modus/pull/460)
- Fix entry alignment issue with AssemblyScript maps [#461](https://github.com/hypermodeinc/modus/pull/461)
- Update to use new Modus manifest [#462](https://github.com/hypermodeinc/modus/pull/462)

## 2024-10-02 - Version 0.12.7

Expand Down
1 change: 1 addition & 0 deletions lib/manifest/collection.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
package manifest

type CollectionInfo struct {
Name string `json:"-"`
SearchMethods map[string]SearchMethodInfo `json:"searchMethods"`
}

Expand Down
17 changes: 17 additions & 0 deletions lib/manifest/connection.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/*
* Copyright 2024 Hypermode Inc.
* Licensed under the terms of the Apache License, Version 2.0
* See the LICENSE file that accompanied this code for further details.
*
* SPDX-FileCopyrightText: 2024 Hypermode Inc. <hello@hypermode.com>
* SPDX-License-Identifier: Apache-2.0
*/

package manifest

type ConnectionInfo interface {
ConnectionName() string
ConnectionType() string
Hash() string
Variables() []string
}
37 changes: 10 additions & 27 deletions lib/manifest/dgraph.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,44 +9,27 @@

package manifest

import (
"crypto/sha256"
"encoding/hex"
"fmt"
)
const ConnectionTypeDgraph string = "dgraph"

const (
HostTypeDgraph string = "dgraph"
)

type DgraphHostInfo struct {
type DgraphConnectionInfo struct {
Name string `json:"-"`
Type string `json:"type"`
GrpcTarget string `json:"grpcTarget"`
Key string `json:"key"`
}

func (p DgraphHostInfo) HostName() string {
return p.Name
func (info DgraphConnectionInfo) ConnectionName() string {
return info.Name
}

func (DgraphHostInfo) HostType() string {
return HostTypeDgraph
func (info DgraphConnectionInfo) ConnectionType() string {
return info.Type
}

func (h DgraphHostInfo) GetVariables() []string {
return extractVariables(h.Key)
func (info DgraphConnectionInfo) Hash() string {
return computeHash(info.Name, info.Type, info.GrpcTarget)
}

func (h DgraphHostInfo) Hash() string {
// Concatenate the attributes into a single string
data := fmt.Sprintf("%v|%v|%v", h.Name, h.Type, h.GrpcTarget)

// Compute the SHA-256 hash
hash := sha256.Sum256([]byte(data))

// Convert the hash to a hexadecimal string
hashStr := hex.EncodeToString(hash[:])

return hashStr
func (info DgraphConnectionInfo) Variables() []string {
return extractVariables(info.Key)
}
65 changes: 15 additions & 50 deletions lib/manifest/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,21 +10,14 @@
package manifest

import (
"crypto/sha256"
"encoding/hex"
"fmt"
"regexp"
)

const (
HostTypeHTTP string = "http"
)
const ConnectionTypeHTTP string = "http"

var (
templateRegex = regexp.MustCompile(`{{\s*(?:base64\((.+?):(.+?)\)|(.+?))\s*}}`)
)
var templateRegex = regexp.MustCompile(`{{\s*(?:base64\((.+?):(.+?)\)|(.+?))\s*}}`)

type HTTPHostInfo struct {
type HTTPConnectionInfo struct {
Name string `json:"-"`
Type string `json:"type"`
Endpoint string `json:"endpoint"`
Expand All @@ -33,20 +26,24 @@ type HTTPHostInfo struct {
QueryParameters map[string]string `json:"queryParameters"`
}

func (h HTTPHostInfo) HostName() string {
return h.Name
func (info HTTPConnectionInfo) ConnectionName() string {
return info.Name
}

func (h HTTPHostInfo) HostType() string {
return HostTypeHTTP
func (info HTTPConnectionInfo) ConnectionType() string {
return info.Type
}

func (h HTTPHostInfo) GetVariables() []string {
cap := 2 * (len(h.Headers) + len(h.QueryParameters))
func (info HTTPConnectionInfo) Hash() string {
return computeHash(info.Name, info.Type, info.Endpoint, info.BaseURL)
}

func (info HTTPConnectionInfo) Variables() []string {
cap := 2 * (len(info.Headers) + len(info.QueryParameters))
set := make(map[string]bool, cap)
results := make([]string, 0, cap)

for _, header := range h.Headers {
for _, header := range info.Headers {
vars := extractVariables(header)
for _, v := range vars {
if _, ok := set[v]; !ok {
Expand All @@ -56,7 +53,7 @@ func (h HTTPHostInfo) GetVariables() []string {
}
}

for _, v := range h.QueryParameters {
for _, v := range info.QueryParameters {
vars := extractVariables(v)
for _, v := range vars {
if _, ok := set[v]; !ok {
Expand All @@ -68,35 +65,3 @@ func (h HTTPHostInfo) GetVariables() []string {

return results
}

func (h HTTPHostInfo) Hash() string {
// Concatenate the attributes into a single string
// NOTE: The Type field is not included in the HTTP host hash, for backwards compatibility. It should be include for other host types.
data := fmt.Sprintf("%v|%v|%v|%v|%v", h.Name, h.Endpoint, h.BaseURL, h.Headers, h.QueryParameters)

// Compute the SHA-256 hash
hash := sha256.Sum256([]byte(data))

// Convert the hash to a hexadecimal string
hashStr := hex.EncodeToString(hash[:])

return hashStr
}

func extractVariables(s string) []string {
matches := templateRegex.FindAllStringSubmatch(s, -1)
if matches == nil {
return []string{}
}

results := make([]string, 0, len(matches)*2)
for _, match := range matches {
for j := 1; j < len(match); j++ {
if match[j] != "" {
results = append(results, match[j])
}
}
}

return results
}
118 changes: 57 additions & 61 deletions lib/manifest/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,36 +23,29 @@ import (
// to the manifest schema. In general, we don't consider the schema to be versioned,
// from the user's perspective, so this should be rare.
// NOTE: We intentionally do not expose the *current* version number outside this package.
const currentVersion = 2
const currentVersion = 1

//go:embed hypermode.json
//go:embed modus_schema.json
var schemaContent string

type HostInfo interface {
HostName() string
HostType() string
GetVariables() []string
Hash() string
}

type Manifest struct {
Version int `json:"-"`
Models map[string]ModelInfo `json:"models"`
Hosts map[string]HostInfo `json:"hosts"`
Connections map[string]ConnectionInfo `json:"connections"`
Collections map[string]CollectionInfo `json:"collections"`
}

func (m *Manifest) IsCurrentVersion() bool {
return m.Version == currentVersion
}

func (m *Manifest) GetHostVariables() map[string][]string {
results := make(map[string][]string, len(m.Hosts))
func (m *Manifest) GetVariables() map[string][]string {
results := make(map[string][]string, len(m.Connections))

for _, host := range m.Hosts {
vars := host.GetVariables()
for _, c := range m.Connections {
vars := c.Variables()
if len(vars) > 0 {
results[host.HostName()] = vars
results[c.ConnectionName()] = vars
}
}

Expand All @@ -64,23 +57,22 @@ func IsCurrentVersion(version int) bool {
}

func ValidateManifest(content []byte) error {
sch, err := jsonschema.CompileString("hypermode.json", schemaContent)

sch, err := jsonschema.CompileString("modus.json", schemaContent)
if err != nil {
return err
}

content, err = standardizeJSON(content)
if err != nil {
if content, err := standardizeJSON(content); err != nil {
return fmt.Errorf("failed to standardize manifest: %w", err)
}

var v interface{}
if err := json.Unmarshal(content, &v); err != nil {
return fmt.Errorf("failed to deserialize manifest: %w", err)
}

if err := sch.Validate(v); err != nil {
return fmt.Errorf("failed to validate manifest: %w", err)
} else {
var v interface{}
if err := json.Unmarshal(content, &v); err != nil {
return fmt.Errorf("failed to deserialize manifest: %w", err)
}
if err := sch.Validate(v); err != nil {
return fmt.Errorf("failed to validate manifest: %w", err)
}
}

return nil
Expand All @@ -93,68 +85,72 @@ func ReadManifest(content []byte) (*Manifest, error) {
}

var manifest Manifest
errParse := parseManifestJson(data, &manifest)
if errParse == nil {
return &manifest, nil
if err := parseManifestJson(data, &manifest); err != nil {
return nil, fmt.Errorf("failed to parse manifest: %w", err)
}

return nil, fmt.Errorf("failed to parse manifest: %w", errParse)
return &manifest, nil
}

func parseManifestJson(data []byte, manifest *Manifest) error {
var m struct {
Models map[string]ModelInfo `json:"models"`
Hosts map[string]json.RawMessage `json:"hosts"`
Connections map[string]json.RawMessage `json:"connections"`
Collections map[string]CollectionInfo `json:"collections"`
}
if err := json.Unmarshal(data, &m); err != nil {
return fmt.Errorf("failed to parse manifest: %w", err)
}

manifest.Version = currentVersion
manifest.Models = m.Models
manifest.Collections = m.Collections

// Copy map keys to Name fields
manifest.Models = m.Models
for key, model := range manifest.Models {
model.Name = key
manifest.Models[key] = model
}
for key, collection := range manifest.Collections {
collection.Name = key
manifest.Collections[key] = collection
}

// parse the hosts
manifest.Hosts = make(map[string]HostInfo, len(m.Hosts))
for name, rawHost := range m.Hosts {
hostType := gjson.GetBytes(rawHost, "type")
// Parse the connections by type
manifest.Connections = make(map[string]ConnectionInfo, len(m.Connections))
for name, rawCon := range m.Connections {
t := gjson.GetBytes(rawCon, "type")
if !t.Exists() {
return fmt.Errorf("missing type for connection [%s]", name)
}
conType := t.String()

switch hostType.String() {
case HostTypeHTTP, "":
var h HTTPHostInfo
if err := json.Unmarshal(rawHost, &h); err != nil {
return fmt.Errorf("failed to parse manifest: %w", err)
switch conType {
case ConnectionTypeHTTP:
var info HTTPConnectionInfo
if err := json.Unmarshal(rawCon, &info); err != nil {
return fmt.Errorf("failed to parse http connection [%s]: %w", name, err)
}
h.Name = name
h.Type = HostTypeHTTP
manifest.Hosts[name] = h
case HostTypePostgresql:
var h PostgresqlHostInfo
if err := json.Unmarshal(rawHost, &h); err != nil {
return fmt.Errorf("failed to parse manifest: %w", err)
info.Name = name
manifest.Connections[name] = info
case ConnectionTypePostgresql:
var info PostgresqlConnectionInfo
if err := json.Unmarshal(rawCon, &info); err != nil {
return fmt.Errorf("failed to parse postgresql connection [%s]: %w", name, err)
}
h.Name = name
manifest.Hosts[name] = h
case HostTypeDgraph:
var h DgraphHostInfo
if err := json.Unmarshal(rawHost, &h); err != nil {
return fmt.Errorf("failed to parse manifest: %w", err)
info.Name = name
manifest.Connections[name] = info
case ConnectionTypeDgraph:
var info DgraphConnectionInfo
if err := json.Unmarshal(rawCon, &info); err != nil {
return fmt.Errorf("failed to parse dgraph connection [%s]: %w", name, err)
}
h.Name = name
manifest.Hosts[name] = h
info.Name = name
manifest.Connections[name] = info
default:
return fmt.Errorf("unknown host type: [%s]", hostType.String())
return fmt.Errorf("unknown type [%s] for connection [%s]", conType, name)
}
}

manifest.Collections = m.Collections

return nil
}

Expand Down
Loading
Loading