Skip to content

Commit d0fc25d

Browse files
authored
AAS Repository (#204)
* Implement first batch of AAS Repository integration tests * Implement non-superpath methods and some superpath methods of AAS Repository * Include AAS Repository * Remove AAS repo superpath endpoint implementation * Remove superpath related code * Fix linter errors * Fix linter errors * Fix errors * Delete unused functions * Fix linter error * Format files * Revert submodel database * Include improvements * Fix linter error * Implement improvements * Implement improvements * Fix linter errors * Fix error * Fix encoding and decoding * Add accidentally removed line * Remove unnecessary error handling * Implement description endpoint * Add empty lines
1 parent 40a240c commit d0fc25d

File tree

79 files changed

+6949
-2444
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

79 files changed

+6949
-2444
lines changed

.github/workflows/go-tests.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -391,3 +391,23 @@ jobs:
391391
cd internal/submodelrepository/security_tests
392392
go test -v
393393
394+
aas-repository-integration-tests:
395+
name: Asset Administration Shell Repository Integration Tests
396+
runs-on: ubuntu-latest
397+
steps:
398+
- name: Check out code
399+
uses: actions/checkout@v6
400+
401+
- name: Set up Go
402+
uses: actions/setup-go@v6
403+
with:
404+
go-version-file: go.mod
405+
cache: true
406+
407+
- name: Get dependencies
408+
run: go mod download
409+
410+
- name: Run aas repository integration tests
411+
run: |
412+
cd internal/aasrepository/integration_tests
413+
go test -v .

basyxschema.sql

Lines changed: 45 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,8 +56,7 @@ CREATE TABLE IF NOT EXISTS aas (
5656
id_short varchar(128),
5757
category varchar(128),
5858
model_type int NOT NULL DEFAULT 3
59-
-- submodel missing
60-
); -- asset_information in asset_information
59+
);
6160

6261
CREATE TABLE IF NOT EXISTS aas_payload (
6362
aas_id BIGINT PRIMARY KEY REFERENCES aas(id) ON DELETE CASCADE,
@@ -74,9 +73,42 @@ CREATE TABLE IF NOT EXISTS asset_information (
7473
asset_kind int,
7574
global_asset_id varchar(2048),
7675
asset_type varchar(2048),
77-
default_thumbnail JSONB,
7876
model_type int NOT NULL DEFAULT 4
79-
); -- specific_asset_id in specific_asset_id
77+
);
78+
79+
CREATE TABLE IF NOT EXISTS aas_submodel_reference (
80+
id BIGSERIAL PRIMARY KEY,
81+
aas_id BIGINT NOT NULL REFERENCES aas(id) ON DELETE CASCADE,
82+
type int NOT NULL
83+
);
84+
85+
CREATE TABLE IF NOT EXISTS aas_submodel_reference_key (
86+
id BIGSERIAL PRIMARY KEY,
87+
reference_id BIGINT NOT NULL REFERENCES aas_submodel_reference(id) ON DELETE CASCADE,
88+
position INTEGER NOT NULL,
89+
type int NOT NULL,
90+
value TEXT NOT NULL,
91+
UNIQUE(reference_id, position)
92+
);
93+
94+
CREATE TABLE IF NOT EXISTS aas_submodel_reference_payload (
95+
id BIGSERIAL PRIMARY KEY,
96+
reference_id BIGINT NOT NULL REFERENCES aas_submodel_reference(id) ON DELETE CASCADE,
97+
parent_reference_payload JSONB NOT NULL,
98+
UNIQUE(reference_id)
99+
);
100+
101+
CREATE TABLE IF NOT EXISTS thumbnail_file_element (
102+
id BIGINT PRIMARY KEY REFERENCES asset_information(asset_information_id) ON DELETE CASCADE,
103+
content_type TEXT,
104+
file_name TEXT,
105+
value TEXT
106+
);
107+
108+
CREATE TABLE IF NOT EXISTS thumbnail_file_data (
109+
id BIGINT PRIMARY KEY REFERENCES thumbnail_file_element(id) ON DELETE CASCADE,
110+
file_oid oid
111+
);
80112
--
81113
-- ------------------------------------------
82114

@@ -497,6 +529,13 @@ CREATE TABLE IF NOT EXISTS submodel_descriptor_supplemental_semantic_id_referenc
497529
-- Indexes
498530
-- ------------------------------------------
499531

532+
CREATE INDEX IF NOT EXISTS ix_aas_identifier ON aas(aas_id);
533+
CREATE INDEX IF NOT EXISTS ix_aas_idshort ON aas(id_short);
534+
535+
CREATE INDEX IF NOT EXISTS ix_asset_information_asset_kind ON asset_information(asset_kind);
536+
CREATE INDEX IF NOT EXISTS ix_asset_information_asset_type ON asset_information(asset_type);
537+
CREATE INDEX IF NOT EXISTS ix_asset_information_global_asset_id ON asset_information(global_asset_id);
538+
500539
CREATE INDEX IF NOT EXISTS ix_sm_identifier ON submodel(submodel_identifier);
501540
CREATE INDEX IF NOT EXISTS ix_sm_idshort ON submodel(id_short);
502541

@@ -535,8 +574,9 @@ CREATE INDEX IF NOT EXISTS ix_aas_identifier_created_at ON aas_identifier(create
535574
CREATE INDEX IF NOT EXISTS ix_specasset_descriptor_id_name ON specific_asset_id(descriptor_id, name);
536575
CREATE INDEX IF NOT EXISTS ix_specasset_descriptor_id_position ON specific_asset_id(descriptor_id, position);
537576
CREATE INDEX IF NOT EXISTS ix_specasset_aasref ON specific_asset_id(aasRef);
538-
CREATE INDEX IF NOT EXISTS ix_specasset_aas ON specific_asset_id(asset_information_id);
539577
CREATE INDEX IF NOT EXISTS ix_specasset_name_value_aasref ON specific_asset_id(name, value, aasRef);
578+
CREATE INDEX IF NOT EXISTS ix_specasset_aas ON specific_asset_id(asset_information_id);
579+
CREATE INDEX IF NOT EXISTS ix_specasset_name_value_aas ON specific_asset_id(name, value, asset_information_id);
540580
CREATE INDEX IF NOT EXISTS ix_specasset_descriptor_id ON specific_asset_id(descriptor_id);
541581
CREATE INDEX IF NOT EXISTS ix_specasset_name ON specific_asset_id(name);
542582
CREATE INDEX IF NOT EXISTS ix_specasset_name_value ON specific_asset_id(name, value);
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# Stage 1: Build the application
2+
FROM golang:1.25-alpine AS builder
3+
4+
# Version argument for build-time injection
5+
ARG VERSION=dev
6+
7+
# Install git and SSL certificates for package downloads
8+
RUN apk update && apk add --no-cache git ca-certificates && update-ca-certificates
9+
10+
# Set working directory
11+
WORKDIR /app
12+
13+
# Copy go.mod and go.sum
14+
COPY go.mod go.sum ./
15+
16+
# Download dependencies
17+
RUN go mod download
18+
19+
# Copy the entire project
20+
COPY . .
21+
22+
# Build the application with version information
23+
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo \
24+
-ldflags "-X main.Version=${VERSION} -X main.BuildTime=$(date -u +%Y-%m-%dT%H:%M:%SZ)" \
25+
-o aasrepositoryservice ./cmd/aasrepositoryservice
26+
27+
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo \
28+
-o healthprobe ./internal/healthprobe
29+
30+
# Stage 2: Distroless runtime image
31+
FROM gcr.io/distroless/static:nonroot
32+
33+
WORKDIR /app
34+
35+
# Copy runtime artifacts
36+
COPY --from=builder /app/aasrepositoryservice /app/aasrepositoryservice
37+
COPY --from=builder /app/cmd/aasrepositoryservice/config.yaml /config/config.yaml
38+
COPY --from=builder /app/basyxschema.sql /app/basyxschema.sql
39+
COPY --from=builder /app/healthprobe /bin/healthprobe
40+
COPY --from=builder /app/healthprobe /bin/wget
41+
42+
# Default port (can be overridden by environment)
43+
ENV SERVER_PORT=5004
44+
45+
# Expose the service port (this is just documentation, the actual port is determined by the environment)
46+
EXPOSE ${SERVER_PORT}
47+
48+
# Command to run the application
49+
# Use environment variables or mount a custom config file to /config/config.yaml
50+
CMD ["/app/aasrepositoryservice", "-config", "/config/config.yaml", "-databaseSchema", "/app/basyxschema.sql"]
51+
52+
# Health check runs via healthprobe
53+
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
54+
CMD ["/bin/healthprobe"]
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
#!/bin/sh
2+
docker build -t eclipsebasyx/aasrepository-go:SNAPSHOT ../.. -f Dockerfile

cmd/aasrepositoryservice/config.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ server:
22
port: 5004
33
contextPath: ""
44
host: 0.0.0.0
5+
strictVerification: true # Set to false to disable strict AAS metamodel verification
56

67
postgres:
78
host: localhost
@@ -13,3 +14,11 @@ postgres:
1314
maxOpenConnections: 500
1415
maxIdleConnections: 500
1516
connMaxLifetimeMinutes: 5
17+
18+
# jws:
19+
# privateKeyPath: "./rsa-key.pem"
20+
21+
# swagger:
22+
# contactName: "Eclipse BaSyx"
23+
# contactEmail: "basyx-dev@eclipse.org"
24+
# contactUrl: "https://basyx.org"

cmd/aasrepositoryservice/main.go

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,61 @@
1-
// Package main starts the AAS Repository Service.
2-
// It loads configuration, sets up routes, and runs the HTTP server.
1+
// Package main implements the Asset Administration Shell Repository Service server.
32
package main
43

54
import (
65
"context"
6+
"embed"
77
"flag"
88
"fmt"
99
"log"
1010
"net/http"
1111
"os"
12+
"strconv"
1213

1314
"github.com/go-chi/chi/v5"
1415

15-
aasrepositoryapi "github.com/eclipse-basyx/basyx-go-components/internal/aasrepository/api"
16+
"github.com/eclipse-basyx/basyx-go-components/internal/aasrepository/api"
17+
persistencepostgresql "github.com/eclipse-basyx/basyx-go-components/internal/aasrepository/persistence"
1618
"github.com/eclipse-basyx/basyx-go-components/internal/common"
1719
openapi "github.com/eclipse-basyx/basyx-go-components/pkg/aasrepositoryapi/go"
1820
)
1921

20-
func runServer(ctx context.Context, configPath string, _ string) error {
21-
// _ = databaseSchema // intentionally unused for now
22-
log.Default().Println("Loading AAS Repository Service...")
23-
log.Default().Println("Config Path:", configPath)
22+
//go:embed openapi.yaml
23+
var openapiSpec embed.FS
2424

25+
func runServer(ctx context.Context, configPath string, databaseSchema string) error {
26+
log.Default().Println("Loading Asset Administration Shell Repository Service...")
27+
log.Default().Println("Config Path:", configPath)
28+
// Load configuration
2529
config, err := common.LoadConfig(configPath)
2630
if err != nil {
2731
return err
2832
}
2933

3034
common.PrintConfiguration(config)
3135

36+
// Create Chi router
3237
r := chi.NewRouter()
3338
common.AddDefaultRouterErrorHandlers(r, "AASRepositoryService")
3439
common.AddCors(r, config)
40+
41+
// Add health endpoint
3542
common.AddHealthEndpoint(r, config)
3643

37-
// ==== AAS Repository Service Custom ====
38-
// aasSvc := api.NewAASRepositoryService()
39-
// aasCtrl := openapi.NewAssetAdministrationShellRepositoryAPIAPIController(aasSvc)
44+
// Add Swagger UI
45+
if err := common.AddSwaggerUIFromFS(r, openapiSpec, "openapi.yaml", "Asset Administration Shell Repository API", "/swagger", "/api-docs/openapi.yaml", config); err != nil {
46+
log.Printf("Warning: failed to load OpenAPI spec for Swagger UI: %v", err)
47+
}
48+
49+
// Instantiate generated services & controllers
50+
// ==== Asset Administration Shell Repository Service ====
4051

41-
// ==== AAS Repository Service - currently pointing to the openAPI generated ====
42-
aasSvc := aasrepositoryapi.NewAssetAdministrationShellRepositoryAPIAPIService()
43-
aasCtrl := openapi.NewAssetAdministrationShellRepositoryAPIAPIController(aasSvc)
52+
aasDatabase, err := persistencepostgresql.NewAssetAdministrationShellDatabase("postgres://"+config.Postgres.User+":"+config.Postgres.Password+"@"+config.Postgres.Host+":"+strconv.Itoa(config.Postgres.Port)+"/"+config.Postgres.DBName+"?sslmode=disable", config.Postgres.MaxOpenConnections, config.Postgres.MaxIdleConnections, config.Postgres.ConnMaxLifetimeMinutes, databaseSchema, config.Server.StrictVerification)
53+
if err != nil {
54+
return err
55+
}
56+
57+
aasSvc := api.NewAssetAdministrationShellRepositoryAPIAPIService(*aasDatabase)
58+
aasCtrl := openapi.NewAssetAdministrationShellRepositoryAPIAPIController(aasSvc, config.Server.ContextPath, config.Server.StrictVerification)
4459
for _, rt := range aasCtrl.Routes() {
4560
r.Method(rt.Method, rt.Pattern, rt.HandlerFunc)
4661
}
@@ -52,9 +67,9 @@ func runServer(ctx context.Context, configPath string, _ string) error {
5267
r.Method(rt.Method, rt.Pattern, rt.HandlerFunc)
5368
}
5469

70+
// Start the server
5571
addr := "0.0.0.0:" + fmt.Sprintf("%d", config.Server.Port)
56-
log.Printf("▶️ AAS Repository listening on %s\n", addr)
57-
72+
log.Printf("▶️ Asset Administration Shell Repository listening on %s\n", addr)
5873
// Start server in a goroutine
5974
go func() {
6075
//nolint:gosec // implementing this fix would cause errors.
@@ -70,15 +85,17 @@ func runServer(ctx context.Context, configPath string, _ string) error {
7085

7186
func main() {
7287
ctx := context.Background()
88+
// load config path from flag
7389
configPath := ""
7490
databaseSchema := ""
7591
flag.StringVar(&configPath, "config", "", "Path to config file")
7692
flag.StringVar(&databaseSchema, "databaseSchema", "", "Path to Database Schema")
7793
flag.Parse()
7894

7995
if databaseSchema != "" {
80-
if _, err := os.ReadFile(databaseSchema); err != nil {
81-
_, _ = fmt.Println("The specified database schema path is invalid or not found.")
96+
_, fileError := os.ReadFile(databaseSchema)
97+
if fileError != nil {
98+
_, _ = fmt.Println("The specified database schema path is invalid or the file was not found.")
8299
os.Exit(1)
83100
}
84101
}

0 commit comments

Comments
 (0)