diff --git a/Dockerfile b/Dockerfile index 94c5740..9862021 100644 --- a/Dockerfile +++ b/Dockerfile @@ -24,7 +24,7 @@ RUN go mod download RUN DATE=$(date) && \ GIT_COMMIT=$(git rev-list -1 HEAD) && \ - go build -ldflags "-X 'template/apiservices.BuildTimestamp=$DATE' -X 'template/apiservices.GitCommit=$GIT_COMMIT'" -o ../app + go build -ldflags "-X 'gp-joule/apiservices.BuildTimestamp=$DATE' -X 'gp-joule/apiservices.GitCommit=$GIT_COMMIT'" -o ../app FROM eliona/base-alpine:latest AS target diff --git a/LICENSE.md b/LICENSE.md index ce9acc6..532d76f 100644 --- a/LICENSE.md +++ b/LICENSE.md @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2022 Leicom ITEC AG +Copyright (c) 2024 Leicom ITEC AG Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 55d076b..dbc96cc 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# App Template +# Eliona App for GP Joule -This template is a part of the Eliona App SDK. It can be used to create an app stub for an Eliona environment. +This app connects [GP Joule API](https://www.gp-joule.com/) and gathers information about data of charging stations. ## Configuration @@ -34,11 +34,11 @@ This initialization can be handled by the `reset.sql` script. Todo: Describe other tables if the app needs them. -The app requires configuration data that remains in the database. To do this, the app creates its own database schema `template` during initialization. To modify and handle the configuration data the app provides an API access. Have a look at the [API specification](https://eliona-smart-building-assistant.github.io/open-api-docs/?https://raw.githubusercontent.com/eliona-smart-building-assistant/app-template/develop/openapi.yaml) how the configuration tables should be used. +The app requires configuration data that remains in the database. To do this, the app creates its own database schema `gp_joule` during initialization. To modify and handle the configuration data the app provides an API access. Have a look at the [API specification](https://eliona-smart-building-assistant.github.io/open-api-docs/?https://raw.githubusercontent.com/eliona-smart-building-assistant/gp-joule-app/develop/openapi.yaml) how the configuration tables should be used. -- `template.configuration`: Contains configuration of the app. Editable through the API. +- `gp_joule.configuration`: Contains configuration of the app. Editable through the API. -- `template.asset`: Provides asset mapping. Maps broker's asset IDs to Eliona asset IDs. +- `gp_joule.asset`: Provides asset mapping. Maps broker's asset IDs to Eliona asset IDs. **Generation**: to generate access method to database see Generation section below. @@ -49,7 +49,7 @@ The app requires configuration data that remains in the database. To do this, th The app provides its own API to access configuration data and other functions. The full description of the API is defined in the `openapi.yaml` OpenAPI definition file. -- [API Reference](https://eliona-smart-building-assistant.github.io/open-api-docs/?https://raw.githubusercontent.com/eliona-smart-building-assistant/app-template/develop/openapi.yaml) shows details of the API +- [API Reference](https://eliona-smart-building-assistant.github.io/open-api-docs/?https://raw.githubusercontent.com/eliona-smart-building-assistant/gp-joule-app/develop/openapi.yaml) shows details of the API **Generation**: to generate api server stub see Generation section below. @@ -67,7 +67,7 @@ The data is written for each device, structured into different subtypes of Elino ### Continuous asset creation ### -Assets for all devices connected to the Template account are created automatically when the configuration is added. +Assets for all devices connected to the GP Joule API are created automatically when a configuration is added. To select which assets to create, a filter could be specified in config. The schema of the filter is defined in the `openapi.yaml` file. diff --git a/apiserver/api.go b/apiserver/api.go index 10b42e4..c7dd30c 100644 --- a/apiserver/api.go +++ b/apiserver/api.go @@ -1,7 +1,7 @@ /* - * App template API + * GP Joule app API * - * API to access and configure the app template + * API to access and configure the GP Joule app * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/apiserver/api_configuration.go b/apiserver/api_configuration.go index 5837dd3..b7150bb 100644 --- a/apiserver/api_configuration.go +++ b/apiserver/api_configuration.go @@ -1,7 +1,7 @@ /* - * App template API + * GP Joule app API * - * API to access and configure the app template + * API to access and configure the GP Joule app * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/apiserver/api_customization.go b/apiserver/api_customization.go index 4aa39f6..e78d7fe 100644 --- a/apiserver/api_customization.go +++ b/apiserver/api_customization.go @@ -1,7 +1,7 @@ /* - * App template API + * GP Joule app API * - * API to access and configure the app template + * API to access and configure the GP Joule app * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/apiserver/api_version.go b/apiserver/api_version.go index a70c660..b1685ca 100644 --- a/apiserver/api_version.go +++ b/apiserver/api_version.go @@ -1,7 +1,7 @@ /* - * App template API + * GP Joule app API * - * API to access and configure the app template + * API to access and configure the GP Joule app * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/apiserver/error.go b/apiserver/error.go index 3250fdc..2b10c8d 100644 --- a/apiserver/error.go +++ b/apiserver/error.go @@ -1,7 +1,7 @@ /* - * App template API + * GP Joule app API * - * API to access and configure the app template + * API to access and configure the GP Joule app * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/apiserver/helpers.go b/apiserver/helpers.go index 90f729c..bc3a308 100644 --- a/apiserver/helpers.go +++ b/apiserver/helpers.go @@ -1,7 +1,7 @@ /* - * App template API + * GP Joule app API * - * API to access and configure the app template + * API to access and configure the GP Joule app * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/apiserver/impl.go b/apiserver/impl.go index 87d096c..9f1e116 100644 --- a/apiserver/impl.go +++ b/apiserver/impl.go @@ -1,7 +1,7 @@ /* - * App template API + * GP Joule app API * - * API to access and configure the app template + * API to access and configure the GP Joule app * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/apiserver/logger.go b/apiserver/logger.go index a734d4a..313541b 100644 --- a/apiserver/logger.go +++ b/apiserver/logger.go @@ -1,7 +1,7 @@ /* - * App template API + * GP Joule app API * - * API to access and configure the app template + * API to access and configure the GP Joule app * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/apiserver/model_configuration.go b/apiserver/model_configuration.go index b3b3b6d..5e235d4 100644 --- a/apiserver/model_configuration.go +++ b/apiserver/model_configuration.go @@ -1,7 +1,7 @@ /* - * App template API + * GP Joule app API * - * API to access and configure the app template + * API to access and configure the GP Joule app * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/apiserver/model_dashboard.go b/apiserver/model_dashboard.go index a5dc50c..c8d9246 100644 --- a/apiserver/model_dashboard.go +++ b/apiserver/model_dashboard.go @@ -1,7 +1,7 @@ /* - * App template API + * GP Joule app API * - * API to access and configure the app template + * API to access and configure the GP Joule app * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/apiserver/model_filter_rule.go b/apiserver/model_filter_rule.go index fc8abe0..a83249b 100644 --- a/apiserver/model_filter_rule.go +++ b/apiserver/model_filter_rule.go @@ -1,7 +1,7 @@ /* - * App template API + * GP Joule app API * - * API to access and configure the app template + * API to access and configure the GP Joule app * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/apiserver/model_widget.go b/apiserver/model_widget.go index cd6bea5..98fcc93 100644 --- a/apiserver/model_widget.go +++ b/apiserver/model_widget.go @@ -1,7 +1,7 @@ /* - * App template API + * GP Joule app API * - * API to access and configure the app template + * API to access and configure the GP Joule app * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/apiserver/model_widget_data.go b/apiserver/model_widget_data.go index 41b1294..1548b78 100644 --- a/apiserver/model_widget_data.go +++ b/apiserver/model_widget_data.go @@ -1,7 +1,7 @@ /* - * App template API + * GP Joule app API * - * API to access and configure the app template + * API to access and configure the GP Joule app * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/apiserver/routers.go b/apiserver/routers.go index 9506a95..c0056d5 100644 --- a/apiserver/routers.go +++ b/apiserver/routers.go @@ -1,7 +1,7 @@ /* - * App template API + * GP Joule app API * - * API to access and configure the app template + * API to access and configure the GP Joule app * * API version: 1.0.0 * Generated by: OpenAPI Generator (https://openapi-generator.tech) diff --git a/apiservices/api_configuration_service.go b/apiservices/api_configuration_service.go index 7bf8d4d..fb0aab5 100644 --- a/apiservices/api_configuration_service.go +++ b/apiservices/api_configuration_service.go @@ -18,9 +18,9 @@ package apiservices import ( "context" "errors" + "gp-joule/apiserver" + "gp-joule/conf" "net/http" - "template/apiserver" - "template/conf" ) // ConfigurationAPIService is a service that implements the logic for the ConfigurationAPIServicer diff --git a/apiservices/api_customization_service.go b/apiservices/api_customization_service.go index 73b603c..3a6e755 100644 --- a/apiservices/api_customization_service.go +++ b/apiservices/api_customization_service.go @@ -17,8 +17,8 @@ package apiservices import ( "context" + "gp-joule/apiserver" "net/http" - "template/apiserver" ) // CustomizationAPIService is a service that implements the logic for the CustomizationAPIServicer @@ -34,7 +34,7 @@ func NewCustomizationAPIService() apiserver.CustomizationAPIServicer { // GetDashboardTemplateByName - Get a full dashboard template func (s *CustomizationAPIService) GetDashboardTemplateByName(ctx context.Context, dashboardTemplateName string, projectId string) (apiserver.ImplResponse, error) { - if dashboardTemplateName == "Template" { + if dashboardTemplateName == "GP Joule" { return apiserver.ImplResponse{Code: http.StatusNotImplemented}, nil } else { return apiserver.ImplResponse{Code: http.StatusNotFound}, nil diff --git a/apiservices/api_version_service.go b/apiservices/api_version_service.go index 2df2bd8..27697ea 100644 --- a/apiservices/api_version_service.go +++ b/apiservices/api_version_service.go @@ -18,10 +18,10 @@ package apiservices import ( "context" "encoding/json" + "gp-joule/apiserver" "io" "net/http" "os" - "template/apiserver" "github.com/eliona-smart-building-assistant/go-utils/common" "github.com/eliona-smart-building-assistant/go-utils/log" diff --git a/app.go b/app.go index 4b8a1cc..6ce9433 100644 --- a/app.go +++ b/app.go @@ -17,13 +17,13 @@ package main import ( "context" + "gp-joule/apiserver" + "gp-joule/apiservices" + "gp-joule/appdb" + "gp-joule/conf" + "gp-joule/eliona" "net/http" "sync" - "template/apiserver" - "template/apiservices" - "template/appdb" - "template/conf" - "template/eliona" "time" "github.com/eliona-smart-building-assistant/go-eliona/app" diff --git a/appdb/asset.go b/appdb/asset.go index 7e318d0..d389bad 100644 --- a/appdb/asset.go +++ b/appdb/asset.go @@ -1,4 +1,4 @@ -// Code generated by SQLBoiler 4.16.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// Code generated by SQLBoiler 4.16.2 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package appdb @@ -165,12 +165,12 @@ var AssetWhere = struct { ProviderID whereHelperstring AssetID whereHelpernull_Int32 }{ - ID: whereHelperint64{field: "\"template\".\"asset\".\"id\""}, - ConfigurationID: whereHelperint64{field: "\"template\".\"asset\".\"configuration_id\""}, - ProjectID: whereHelperstring{field: "\"template\".\"asset\".\"project_id\""}, - GlobalAssetID: whereHelperstring{field: "\"template\".\"asset\".\"global_asset_id\""}, - ProviderID: whereHelperstring{field: "\"template\".\"asset\".\"provider_id\""}, - AssetID: whereHelpernull_Int32{field: "\"template\".\"asset\".\"asset_id\""}, + ID: whereHelperint64{field: "\"gp_joule\".\"asset\".\"id\""}, + ConfigurationID: whereHelperint64{field: "\"gp_joule\".\"asset\".\"configuration_id\""}, + ProjectID: whereHelperstring{field: "\"gp_joule\".\"asset\".\"project_id\""}, + GlobalAssetID: whereHelperstring{field: "\"gp_joule\".\"asset\".\"global_asset_id\""}, + ProviderID: whereHelperstring{field: "\"gp_joule\".\"asset\".\"provider_id\""}, + AssetID: whereHelpernull_Int32{field: "\"gp_joule\".\"asset\".\"asset_id\""}, } // AssetRels is where relationship names are stored. @@ -602,8 +602,8 @@ func (assetL) LoadConfiguration(ctx context.Context, e boil.ContextExecutor, sin } query := NewQuery( - qm.From(`template.configuration`), - qm.WhereIn(`template.configuration.id in ?`, argsSlice...), + qm.From(`gp_joule.configuration`), + qm.WhereIn(`gp_joule.configuration.id in ?`, argsSlice...), ) if mods != nil { mods.Apply(query) @@ -684,7 +684,7 @@ func (o *Asset) SetConfiguration(ctx context.Context, exec boil.ContextExecutor, } updateQuery := fmt.Sprintf( - "UPDATE \"template\".\"asset\" SET %s WHERE %s", + "UPDATE \"gp_joule\".\"asset\" SET %s WHERE %s", strmangle.SetParamNames("\"", "\"", 1, []string{"configuration_id"}), strmangle.WhereClause("\"", "\"", 2, assetPrimaryKeyColumns), ) @@ -721,10 +721,10 @@ func (o *Asset) SetConfiguration(ctx context.Context, exec boil.ContextExecutor, // Assets retrieves all the records using an executor. func Assets(mods ...qm.QueryMod) assetQuery { - mods = append(mods, qm.From("\"template\".\"asset\"")) + mods = append(mods, qm.From("\"gp_joule\".\"asset\"")) q := NewQuery(mods...) if len(queries.GetSelect(q)) == 0 { - queries.SetSelect(q, []string{"\"template\".\"asset\".*"}) + queries.SetSelect(q, []string{"\"gp_joule\".\"asset\".*"}) } return assetQuery{q} @@ -745,7 +745,7 @@ func FindAsset(ctx context.Context, exec boil.ContextExecutor, iD int64, selectC sel = strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, selectCols), ",") } query := fmt.Sprintf( - "select %s from \"template\".\"asset\" where \"id\"=$1", sel, + "select %s from \"gp_joule\".\"asset\" where \"id\"=$1", sel, ) q := queries.Raw(query, iD) @@ -807,9 +807,9 @@ func (o *Asset) Insert(ctx context.Context, exec boil.ContextExecutor, columns b return err } if len(wl) != 0 { - cache.query = fmt.Sprintf("INSERT INTO \"template\".\"asset\" (\"%s\") %%sVALUES (%s)%%s", strings.Join(wl, "\",\""), strmangle.Placeholders(dialect.UseIndexPlaceholders, len(wl), 1, 1)) + cache.query = fmt.Sprintf("INSERT INTO \"gp_joule\".\"asset\" (\"%s\") %%sVALUES (%s)%%s", strings.Join(wl, "\",\""), strmangle.Placeholders(dialect.UseIndexPlaceholders, len(wl), 1, 1)) } else { - cache.query = "INSERT INTO \"template\".\"asset\" %sDEFAULT VALUES%s" + cache.query = "INSERT INTO \"gp_joule\".\"asset\" %sDEFAULT VALUES%s" } var queryOutput, queryReturning string @@ -881,7 +881,7 @@ func (o *Asset) Update(ctx context.Context, exec boil.ContextExecutor, columns b return 0, errors.New("appdb: unable to update asset, could not build whitelist") } - cache.query = fmt.Sprintf("UPDATE \"template\".\"asset\" SET %s WHERE %s", + cache.query = fmt.Sprintf("UPDATE \"gp_joule\".\"asset\" SET %s WHERE %s", strmangle.SetParamNames("\"", "\"", 1, wl), strmangle.WhereClause("\"", "\"", len(wl)+1, assetPrimaryKeyColumns), ) @@ -972,7 +972,7 @@ func (o AssetSlice) UpdateAll(ctx context.Context, exec boil.ContextExecutor, co args = append(args, pkeyArgs...) } - sql := fmt.Sprintf("UPDATE \"template\".\"asset\" SET %s WHERE %s", + sql := fmt.Sprintf("UPDATE \"gp_joule\".\"asset\" SET %s WHERE %s", strmangle.SetParamNames("\"", "\"", 1, colNames), strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), len(colNames)+1, assetPrimaryKeyColumns, len(o))) @@ -1073,7 +1073,7 @@ func (o *Asset) Upsert(ctx context.Context, exec boil.ContextExecutor, updateOnC conflict = make([]string, len(assetPrimaryKeyColumns)) copy(conflict, assetPrimaryKeyColumns) } - cache.query = buildUpsertQueryPostgres(dialect, "\"template\".\"asset\"", updateOnConflict, ret, update, conflict, insert, opts...) + cache.query = buildUpsertQueryPostgres(dialect, "\"gp_joule\".\"asset\"", updateOnConflict, ret, update, conflict, insert, opts...) cache.valueMapping, err = queries.BindMapping(assetType, assetMapping, insert) if err != nil { @@ -1138,7 +1138,7 @@ func (o *Asset) Delete(ctx context.Context, exec boil.ContextExecutor) (int64, e } args := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), assetPrimaryKeyMapping) - sql := "DELETE FROM \"template\".\"asset\" WHERE \"id\"=$1" + sql := "DELETE FROM \"gp_joule\".\"asset\" WHERE \"id\"=$1" if boil.IsDebug(ctx) { writer := boil.DebugWriterFrom(ctx) @@ -1212,7 +1212,7 @@ func (o AssetSlice) DeleteAll(ctx context.Context, exec boil.ContextExecutor) (i args = append(args, pkeyArgs...) } - sql := "DELETE FROM \"template\".\"asset\" WHERE " + + sql := "DELETE FROM \"gp_joule\".\"asset\" WHERE " + strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 1, assetPrimaryKeyColumns, len(o)) if boil.IsDebug(ctx) { @@ -1286,7 +1286,7 @@ func (o *AssetSlice) ReloadAll(ctx context.Context, exec boil.ContextExecutor) e args = append(args, pkeyArgs...) } - sql := "SELECT \"template\".\"asset\".* FROM \"template\".\"asset\" WHERE " + + sql := "SELECT \"gp_joule\".\"asset\".* FROM \"gp_joule\".\"asset\" WHERE " + strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 1, assetPrimaryKeyColumns, len(*o)) q := queries.Raw(sql, args...) @@ -1309,7 +1309,7 @@ func AssetExistsG(ctx context.Context, iD int64) (bool, error) { // AssetExists checks if the Asset row exists. func AssetExists(ctx context.Context, exec boil.ContextExecutor, iD int64) (bool, error) { var exists bool - sql := "select exists(select 1 from \"template\".\"asset\" where \"id\"=$1 limit 1)" + sql := "select exists(select 1 from \"gp_joule\".\"asset\" where \"id\"=$1 limit 1)" if boil.IsDebug(ctx) { writer := boil.DebugWriterFrom(ctx) diff --git a/appdb/boil_queries.go b/appdb/boil_queries.go index 0d64354..b3bd11d 100644 --- a/appdb/boil_queries.go +++ b/appdb/boil_queries.go @@ -1,4 +1,4 @@ -// Code generated by SQLBoiler 4.16.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// Code generated by SQLBoiler 4.16.2 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package appdb diff --git a/appdb/boil_table_names.go b/appdb/boil_table_names.go index 60635e9..928b633 100644 --- a/appdb/boil_table_names.go +++ b/appdb/boil_table_names.go @@ -1,4 +1,4 @@ -// Code generated by SQLBoiler 4.16.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// Code generated by SQLBoiler 4.16.2 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package appdb diff --git a/appdb/boil_types.go b/appdb/boil_types.go index b2f6cc4..85d3602 100644 --- a/appdb/boil_types.go +++ b/appdb/boil_types.go @@ -1,4 +1,4 @@ -// Code generated by SQLBoiler 4.16.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// Code generated by SQLBoiler 4.16.2 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package appdb diff --git a/appdb/boil_view_names.go b/appdb/boil_view_names.go index 7c2ce91..f407775 100644 --- a/appdb/boil_view_names.go +++ b/appdb/boil_view_names.go @@ -1,4 +1,4 @@ -// Code generated by SQLBoiler 4.16.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// Code generated by SQLBoiler 4.16.2 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package appdb diff --git a/appdb/configuration.go b/appdb/configuration.go index 0e59818..554196e 100644 --- a/appdb/configuration.go +++ b/appdb/configuration.go @@ -1,4 +1,4 @@ -// Code generated by SQLBoiler 4.16.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// Code generated by SQLBoiler 4.16.2 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package appdb @@ -243,15 +243,15 @@ var ConfigurationWhere = struct { ProjectIds whereHelpertypes_StringArray UserID whereHelpernull_String }{ - ID: whereHelperint64{field: "\"template\".\"configuration\".\"id\""}, - APIAccessChangeMe: whereHelperstring{field: "\"template\".\"configuration\".\"api_access_change_me\""}, - RefreshInterval: whereHelperint32{field: "\"template\".\"configuration\".\"refresh_interval\""}, - RequestTimeout: whereHelperint32{field: "\"template\".\"configuration\".\"request_timeout\""}, - AssetFilter: whereHelpernull_JSON{field: "\"template\".\"configuration\".\"asset_filter\""}, - Active: whereHelpernull_Bool{field: "\"template\".\"configuration\".\"active\""}, - Enable: whereHelpernull_Bool{field: "\"template\".\"configuration\".\"enable\""}, - ProjectIds: whereHelpertypes_StringArray{field: "\"template\".\"configuration\".\"project_ids\""}, - UserID: whereHelpernull_String{field: "\"template\".\"configuration\".\"user_id\""}, + ID: whereHelperint64{field: "\"gp_joule\".\"configuration\".\"id\""}, + APIAccessChangeMe: whereHelperstring{field: "\"gp_joule\".\"configuration\".\"api_access_change_me\""}, + RefreshInterval: whereHelperint32{field: "\"gp_joule\".\"configuration\".\"refresh_interval\""}, + RequestTimeout: whereHelperint32{field: "\"gp_joule\".\"configuration\".\"request_timeout\""}, + AssetFilter: whereHelpernull_JSON{field: "\"gp_joule\".\"configuration\".\"asset_filter\""}, + Active: whereHelpernull_Bool{field: "\"gp_joule\".\"configuration\".\"active\""}, + Enable: whereHelpernull_Bool{field: "\"gp_joule\".\"configuration\".\"enable\""}, + ProjectIds: whereHelpertypes_StringArray{field: "\"gp_joule\".\"configuration\".\"project_ids\""}, + UserID: whereHelpernull_String{field: "\"gp_joule\".\"configuration\".\"user_id\""}, } // ConfigurationRels is where relationship names are stored. @@ -622,7 +622,7 @@ func (o *Configuration) Assets(mods ...qm.QueryMod) assetQuery { } queryMods = append(queryMods, - qm.Where("\"template\".\"asset\".\"configuration_id\"=?", o.ID), + qm.Where("\"gp_joule\".\"asset\".\"configuration_id\"=?", o.ID), ) return Assets(queryMods...) @@ -683,8 +683,8 @@ func (configurationL) LoadAssets(ctx context.Context, e boil.ContextExecutor, si } query := NewQuery( - qm.From(`template.asset`), - qm.WhereIn(`template.asset.configuration_id in ?`, argsSlice...), + qm.From(`gp_joule.asset`), + qm.WhereIn(`gp_joule.asset.configuration_id in ?`, argsSlice...), ) if mods != nil { mods.Apply(query) @@ -764,7 +764,7 @@ func (o *Configuration) AddAssets(ctx context.Context, exec boil.ContextExecutor } } else { updateQuery := fmt.Sprintf( - "UPDATE \"template\".\"asset\" SET %s WHERE %s", + "UPDATE \"gp_joule\".\"asset\" SET %s WHERE %s", strmangle.SetParamNames("\"", "\"", 1, []string{"configuration_id"}), strmangle.WhereClause("\"", "\"", 2, assetPrimaryKeyColumns), ) @@ -805,10 +805,10 @@ func (o *Configuration) AddAssets(ctx context.Context, exec boil.ContextExecutor // Configurations retrieves all the records using an executor. func Configurations(mods ...qm.QueryMod) configurationQuery { - mods = append(mods, qm.From("\"template\".\"configuration\"")) + mods = append(mods, qm.From("\"gp_joule\".\"configuration\"")) q := NewQuery(mods...) if len(queries.GetSelect(q)) == 0 { - queries.SetSelect(q, []string{"\"template\".\"configuration\".*"}) + queries.SetSelect(q, []string{"\"gp_joule\".\"configuration\".*"}) } return configurationQuery{q} @@ -829,7 +829,7 @@ func FindConfiguration(ctx context.Context, exec boil.ContextExecutor, iD int64, sel = strings.Join(strmangle.IdentQuoteSlice(dialect.LQ, dialect.RQ, selectCols), ",") } query := fmt.Sprintf( - "select %s from \"template\".\"configuration\" where \"id\"=$1", sel, + "select %s from \"gp_joule\".\"configuration\" where \"id\"=$1", sel, ) q := queries.Raw(query, iD) @@ -891,9 +891,9 @@ func (o *Configuration) Insert(ctx context.Context, exec boil.ContextExecutor, c return err } if len(wl) != 0 { - cache.query = fmt.Sprintf("INSERT INTO \"template\".\"configuration\" (\"%s\") %%sVALUES (%s)%%s", strings.Join(wl, "\",\""), strmangle.Placeholders(dialect.UseIndexPlaceholders, len(wl), 1, 1)) + cache.query = fmt.Sprintf("INSERT INTO \"gp_joule\".\"configuration\" (\"%s\") %%sVALUES (%s)%%s", strings.Join(wl, "\",\""), strmangle.Placeholders(dialect.UseIndexPlaceholders, len(wl), 1, 1)) } else { - cache.query = "INSERT INTO \"template\".\"configuration\" %sDEFAULT VALUES%s" + cache.query = "INSERT INTO \"gp_joule\".\"configuration\" %sDEFAULT VALUES%s" } var queryOutput, queryReturning string @@ -965,7 +965,7 @@ func (o *Configuration) Update(ctx context.Context, exec boil.ContextExecutor, c return 0, errors.New("appdb: unable to update configuration, could not build whitelist") } - cache.query = fmt.Sprintf("UPDATE \"template\".\"configuration\" SET %s WHERE %s", + cache.query = fmt.Sprintf("UPDATE \"gp_joule\".\"configuration\" SET %s WHERE %s", strmangle.SetParamNames("\"", "\"", 1, wl), strmangle.WhereClause("\"", "\"", len(wl)+1, configurationPrimaryKeyColumns), ) @@ -1056,7 +1056,7 @@ func (o ConfigurationSlice) UpdateAll(ctx context.Context, exec boil.ContextExec args = append(args, pkeyArgs...) } - sql := fmt.Sprintf("UPDATE \"template\".\"configuration\" SET %s WHERE %s", + sql := fmt.Sprintf("UPDATE \"gp_joule\".\"configuration\" SET %s WHERE %s", strmangle.SetParamNames("\"", "\"", 1, colNames), strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), len(colNames)+1, configurationPrimaryKeyColumns, len(o))) @@ -1157,7 +1157,7 @@ func (o *Configuration) Upsert(ctx context.Context, exec boil.ContextExecutor, u conflict = make([]string, len(configurationPrimaryKeyColumns)) copy(conflict, configurationPrimaryKeyColumns) } - cache.query = buildUpsertQueryPostgres(dialect, "\"template\".\"configuration\"", updateOnConflict, ret, update, conflict, insert, opts...) + cache.query = buildUpsertQueryPostgres(dialect, "\"gp_joule\".\"configuration\"", updateOnConflict, ret, update, conflict, insert, opts...) cache.valueMapping, err = queries.BindMapping(configurationType, configurationMapping, insert) if err != nil { @@ -1222,7 +1222,7 @@ func (o *Configuration) Delete(ctx context.Context, exec boil.ContextExecutor) ( } args := queries.ValuesFromMapping(reflect.Indirect(reflect.ValueOf(o)), configurationPrimaryKeyMapping) - sql := "DELETE FROM \"template\".\"configuration\" WHERE \"id\"=$1" + sql := "DELETE FROM \"gp_joule\".\"configuration\" WHERE \"id\"=$1" if boil.IsDebug(ctx) { writer := boil.DebugWriterFrom(ctx) @@ -1296,7 +1296,7 @@ func (o ConfigurationSlice) DeleteAll(ctx context.Context, exec boil.ContextExec args = append(args, pkeyArgs...) } - sql := "DELETE FROM \"template\".\"configuration\" WHERE " + + sql := "DELETE FROM \"gp_joule\".\"configuration\" WHERE " + strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 1, configurationPrimaryKeyColumns, len(o)) if boil.IsDebug(ctx) { @@ -1370,7 +1370,7 @@ func (o *ConfigurationSlice) ReloadAll(ctx context.Context, exec boil.ContextExe args = append(args, pkeyArgs...) } - sql := "SELECT \"template\".\"configuration\".* FROM \"template\".\"configuration\" WHERE " + + sql := "SELECT \"gp_joule\".\"configuration\".* FROM \"gp_joule\".\"configuration\" WHERE " + strmangle.WhereClauseRepeated(string(dialect.LQ), string(dialect.RQ), 1, configurationPrimaryKeyColumns, len(*o)) q := queries.Raw(sql, args...) @@ -1393,7 +1393,7 @@ func ConfigurationExistsG(ctx context.Context, iD int64) (bool, error) { // ConfigurationExists checks if the Configuration row exists. func ConfigurationExists(ctx context.Context, exec boil.ContextExecutor, iD int64) (bool, error) { var exists bool - sql := "select exists(select 1 from \"template\".\"configuration\" where \"id\"=$1 limit 1)" + sql := "select exists(select 1 from \"gp_joule\".\"configuration\" where \"id\"=$1 limit 1)" if boil.IsDebug(ctx) { writer := boil.DebugWriterFrom(ctx) diff --git a/appdb/psql_upsert.go b/appdb/psql_upsert.go index 4482228..efe1a8a 100644 --- a/appdb/psql_upsert.go +++ b/appdb/psql_upsert.go @@ -1,4 +1,4 @@ -// Code generated by SQLBoiler 4.16.1 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. +// Code generated by SQLBoiler 4.16.2 (https://github.com/volatiletech/sqlboiler). DO NOT EDIT. // This file is meant to be re-generated in place and/or deleted at any time. package appdb diff --git a/broker/broker.go b/broker/broker.go index 22077ca..414d122 100644 --- a/broker/broker.go +++ b/broker/broker.go @@ -16,8 +16,8 @@ package broker import ( - "template/apiserver" - "template/model" + "gp-joule/apiserver" + "gp-joule/model" ) func GetDevices(config apiserver.Configuration) (model.Root, error) { diff --git a/conf/conf.go b/conf/conf.go index 0390713..94a611a 100644 --- a/conf/conf.go +++ b/conf/conf.go @@ -20,8 +20,8 @@ import ( "encoding/json" "errors" "fmt" - "template/apiserver" - "template/appdb" + "gp-joule/apiserver" + "gp-joule/appdb" "github.com/eliona-smart-building-assistant/go-eliona/frontend" "github.com/eliona-smart-building-assistant/go-utils/common" diff --git a/conf/init.sql b/conf/init.sql index a59c6fe..76e0971 100644 --- a/conf/init.sql +++ b/conf/init.sql @@ -13,10 +13,10 @@ -- DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -create schema if not exists template; +create schema if not exists gp_joule; -- Should be editable by eliona frontend. -create table if not exists template.configuration +create table if not exists gp_joule.configuration ( id bigserial primary key, api_access_change_me text not null, @@ -29,10 +29,10 @@ create table if not exists template.configuration user_id text ); -create table if not exists template.asset +create table if not exists gp_joule.asset ( id bigserial primary key, - configuration_id bigserial not null references template.configuration(id) ON DELETE CASCADE, + configuration_id bigserial not null references gp_joule.configuration(id) ON DELETE CASCADE, project_id text not null, global_asset_id text not null, provider_id text not null, diff --git a/eliona/assets.go b/eliona/assets.go index 050d439..9594e39 100644 --- a/eliona/assets.go +++ b/eliona/assets.go @@ -17,7 +17,7 @@ package eliona import ( "fmt" - "template/apiserver" + "gp-joule/apiserver" api "github.com/eliona-smart-building-assistant/go-eliona-api-client/v2" "github.com/eliona-smart-building-assistant/go-eliona/asset" @@ -48,8 +48,8 @@ func notifyUser(userId string, projectId string, assetsCreated int) error { User: userId, ProjectId: *api.NewNullableString(&projectId), Message: *api.NewNullableTranslation(&api.Translation{ - De: api.PtrString(fmt.Sprintf("Template App hat %d neue Assets angelegt. Diese sind nun im Asset-Management verfügbar.", assetsCreated)), - En: api.PtrString(fmt.Sprintf("Template app added %v new assets. They are now available in Asset Management.", assetsCreated)), + De: api.PtrString(fmt.Sprintf("GP Joule App hat %d neue Assets angelegt. Diese sind nun im Asset-Management verfügbar.", assetsCreated)), + En: api.PtrString(fmt.Sprintf("GP Joule app added %v new assets. They are now available in Asset Management.", assetsCreated)), }), }). Execute() diff --git a/eliona/data.go b/eliona/data.go index a7e42ed..30b7fc3 100644 --- a/eliona/data.go +++ b/eliona/data.go @@ -3,15 +3,15 @@ package eliona import ( "context" "fmt" - "template/apiserver" - "template/conf" - "template/model" + "gp-joule/apiserver" + "gp-joule/conf" + "gp-joule/model" "github.com/eliona-smart-building-assistant/go-eliona/asset" "github.com/eliona-smart-building-assistant/go-utils/log" ) -const ClientReference string = "template-app" +const ClientReference string = "gp-joule-app" func UpsertAssetData(config apiserver.Configuration, assets []model.ExampleDevice) error { for _, projectId := range *config.ProjectIDs { diff --git a/go.mod b/go.mod index 0ed94be..995e1f8 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module template +module gp-joule go 1.20 diff --git a/icon b/icon index 544686f..7b8909e 100644 --- a/icon +++ b/icon @@ -1 +1 @@ -data:image/jpeg;base64,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 \ No newline at end of file +data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAPoAAABWCAIAAABpUBz/AAAAB3RJTUUH6AIWDBg57XWBWQAAAAlwSFlzAAAOwwAADsMBx2+oZAAAAARnQU1BAACxjwv8YQUAACWjSURBVHjaxF0JdE1X93/vCUqooSQkMpiTlJRoTUWQmOdUkKghVtpS1DLUomnT5aM1tKimiCxDVKWllpnyGWL4iyEhNbTmRNQQaozW7N3/773DdXPfe+fse9+Nb6+st17uO/ecvffZZ09nMkuSZKKB1Wr966+/9u7dm52dffLkyZycnFu3bj158sTT0zMwMDAsLKx9+/YRERGvvfaa2WwWVlVQULB58+bdu3efPn366tWreFLGDsWLFy9RosTrdihlB3wpV65cxYoVAwICatas6e3tbbFYiDjrA/AkLy8vMTERiP3777937tx58ODBs2fPgCR+LVasGAgEDkBpy5YtlSpV0tfElStXjh8/fvHiRTTh4eEBSsE6Ly8vEOvj41OhQgUwVshJFZw4cWLdunW5ubnA/NGjR0AYNaAtfEFnXbp0qXv37kuWLHGsFmXmzZs3efJkfLfYQdVf+GzcuPGKFStKliwpv3Lz5k08yczMRM23b9/+559/IA+gonTp0mXLlgVF9evXR53oUzohqPbChQujR48+e/Ysuh5VMWSePn2K+tHK48ePmdCiIyAqZe2A7+zdhw8fXr9+HcXQa+AkWh8+fHih2oUAGrZu3QpRRq/wca1Wrdr06dPv3bsHBnEqBOvfeOMNHVICqvz8/Pr06ZOcnAwWQwQp+GsFIP/1119T8AFb+JSqqkVn7NixA30ZHBzMH7SQqurVq/fs2fObb745deoUOpvSRI8ePfgIQ9ydMg24RUZG8t+FyEILyK+gnl69eglZtH37djqLGCaDBg3SIRtOASMBg0SuXCDu4PKBAwfatGmjSc0EBQXt27ePQyQUm1a95QgQCPQuBA6j0QAZL8xxpueE3b9r1y5hX6IAFFJ6evrIkSOhDnRQikEOXQO5EVIKA8uvCprCKcJ4GB4eLsQEJl35SnR0tPAVEK5J3CFyMOBuyoYSYOtI4g4lPWLECKFGdwqwQWlpaa60L2yNUcRA5t5+++2NGzcaqOnRPXPmzKE0DVeE05f4CQK6cOHCWrVqGULpgAEDwDoO5q1bt+ZXAsVpoLgX8hNcwG+//aZJ3C9fvmyss3ru3Dm5cpf1wk1v27bt3LlzMdp0tHH//v3BgwevX79echYbaHLm+ID6s7KyoOahO9GoUTyC10gpZraD05/Qx3AfYe7j4+PBcUMoXbZsGdR8fn6+qzIIA/iVwLi7+gkaSoiDShiqVKkifAVqSBOZiAckcjxJAcQw8nfn4g6lBbYiBHGnYVjwuLg4BByOlWD4GiXuDMBTRFoQ+rt37xpSIYJmYRmm4Tjkw9bD7BhIJiA7OxsOPQyv409ARijuGMauOI8QU9i6StxZgCjkEp06FAYJxoq7kldOxP3GjRtdunSBmLrfEtQblK5TcadwSisgCoyKijJEx3t5ebnzOiQPhAMfw2kEMw8ePDhhwgTHkQY5RijMfx121dVPlBGuAqejTgVatbvhaTeEqi8rV/0Gzd+/f/8TJ04Y1RgCLAQrKomHrJcqVcpYqkx2UUBbEAWtLHYEmGmh/WE5PsfnEMSZM2cuWrTIcAJlWLBgwf79+x3JVxpup8DRMhUqVBC2q9Lut27dEr6iqS/A0pIlSxpr+R88eCB/LyTuwOzLL7/ctm2bgY2h7+fPn68SC4zgohB3k73Lk5OTT5065U4lYDfFkTUV9gtlelevXp2QkGCsRVYBegpNOIZVQu3OEXdKjyDyVv5LieuEKKmAE13oA5e+OzQx1JLh/YTYXDnCTHZ54lhVNwFdsnjxYjcrIQbTqu5nM3EfffSR6jkH5Fa0qrQ9e/bAq1E9FGr3EiVKuPqJIu4qD4oTusggDCdU4Onpqam8EOR5McDLJGNBQcGwYcP05WH4AFk/duxYs2bNlA+LwneXYc2aNTNmzHCnCeJoVIkXNMXo0aMpJh72LSAgoG/fvk2bNkWcALlByHHmzJmsrCxoh+vXrwuVDgpgVL/77rvyE6h8Yfdx4lHKxJ8O8aD490pAMA0kVfpRN4DPyizwc3Fn7mZubi6lCughKIly5crdvn1bnlrnADoGHYl+VSowoTJr1KhRTEwMvty9e/fq1asXL16EMsOYpBgfEIIWg4ODdbOJqGuVlhqIHTp0aN26dcK3wL3ExMSxY8cyxSO3FRkZKdmnwb///vtJkyYJ3QC0NXfuXFmCUV7oKHPEnRKqqqSQolC0Zg7AE+gavriDYyCEqSSWUMc4hEVlCX6ZpUAvPj6+Tp06L99kpaFOIL5CVFBL5cqVk5KSmKBfunTpk08+odA8ZcoU5VwDvteoUYP/CkI9qwLQHOR+2rRplHwZAEi6M9MEDU1JEezcuVOmCxzv06cPpTtXrVqFwpzJFxALHU+hlKUBGNy4cUOI87Jly1zNiKWlpQmbYxMp8itDhw4VvvL5559rXUQgDJqrVq168+ZNpmrxCUGHy4Qn1+wA5QjJhMqDVKsmH59zJyUlRZixhqzDIcnOzh4+fHj58uXBWR8fn1mzZqWmpgpnXvPy8hxr47/CaDa/ADQHMzd+/PilS5dSBPHAgQPCMnxiKcWU2hQM3LBhg7BaeFm9evVi68xcFQOB7du3/+qrr4Ro7N27V/4u6zYOuJmIVBkcpVvsCoThhCMIq4XOhXiw2Rt8QvxKly5dsWJFLztUqVLF19c3MDAQxVSi8nyt2ZIlS4RItGrVCioHA0sZXaG62NjYuLg4/ruO7qzQ7DodQmixd+/eLVq0EGLr5kQmOE4ZVEpHbteuXUKPs0mTJgiQKDWjDNSK0AYeOXLEKTKugBOPUmJEegguw+PHj7W+IhR3HXUysPE9MzPz/Pnz/HLg+8qVK6EAVF3FVC+MGl8Pwb5oxcxVhXjer18/4eto0Z0UEzFUlSUMbTkmwh0B1omekoKLP2DAAH4ZZcqV2Wt+eY4Kp2RmVKEqZYDpmDYqWnHftGkTvxAk7IcffqhUqZIr1ENDQ/lxvXJmiwiuWAlkWrZsKXzdzdDecc23U5D5Djk7fvw4vzCcsQ4dOtBxYJTy9QgieFnKKcObE6HpWERAETtO6pNDuCY06GBhM5H8Qs2bN4cr6ar7gRw8Uf6s+507d/Th5xT8/f2FOpKi6jjApveExWTHFOWvXLnCLxwWFkaMs2UQOjP379+Xgy6K8HG0O0UudbBUK8kmgmnV4VMxsEALHj16lFMCHdm3b1/hgEPwyvkVgTPF8CmBP/8nZCLL5+hjCoUiBnLoJtnXS/MLY5RqtewgUyhhsiqh0MuRpCLaI6ZjPlGICcu36EDGgpBOOO/Vrl07PgYYDIiLNdEs7BsOmyiaG6rOzZUzlHUESu0u7CQd015ENcaUESUHwkFS6/QnEXRodyGwvV06XvQQrnyEy167dm1+GbSN8NHPzw8iAtYDGxgNsA/KDx2Gbu7cubPKPgjR5djWh3bgv44CQIPikLgCyruyOIIcoTVgu8g0LRa4fPmysIyHhwerliIBnNZ1RDuUFoticRSbtdChPjwQ6/BLhISECPUWmNjfDgaSBHF31Tf5+flCzc3ZeEEESj8p3WUfHx9lWtAR/vzzT03ijsKnT5/ml0HXVK5cWS7vDr0U118HS3VoHGErQFWfp2oRirshG890gCtnBp1KWUdetmxZHTkBJWgSd/TQm2++ye8niDtFWysp3b17N79MhQoV5BWzlLWHnCFBcZx0CJkOcReqV7BdTkkJQfmih+N8pwoCAgK0mmBDgJ2v4OgCwebOmTNHqMlglPTtspWB4nQqffd33nmHj5Vk3303ceJECjPZooDVq1fzi2lVRpwUHsU4qIwqRfqLYi0gUO3QoUPXrl3habPEIHSTp6cnugxj/tatW/fu3cMnBCAmJqZZs2Yywz2E6TMd+QRDwHH7NltMMn78+DNnzghfp+Tm+UARd6VGRIuwJ3yXYPbs2bGxsdAgfIlnK3BGjhwpzPa89dZbmojiqHCKdteR8NbhYglHCOrMzc1NSkoSVvXrr79eunRJVnwW/MMpjV6hbHLRAUIuyHZftkoI9UaMGOG4WcQpdO/e3U0MKb6QcrkfNI1qkbMjQGEjwuHsPmaUotqPP/541apVQvWpbJHifHPWJ1KMoUq7UzpCx5oZAw3C33//rVzAYhGegSFHQsaCsCOVexdggjCUGzZsmJKSQjGgQUFBcC3cxJCSMFZmM9jBGEJHJSMjIzw8HOGHU1mB+ty0aVPz5s3ZglB+VRDQtm3byv9ShI9jLigultb0mknXhLqB4g4eQuJfcoy/HBn0cI6gYN4FyzYWFBTk5+djJF17AdevX4eGhvXw9fVNS0vz8fHRhCh0G4YiKs/JyTl37pymIGnQoEHuO2CUGlTJu+jo6AkTJkCF899CzNquXbsGDRrA+0SM4eXlBQaCV9nZ2Vu3bqUvbsOo8PPz00QUR9wpw1vlzFDEHYKhCUOTruiWA4XEXVjaMUEBItExQ4cOhSDCgMJasTyoqxrQf9BVn3/+uawb2Djht4uOWbt2rQ7yypUrFxcX535sTRF3ZfejxTJlysAJmTx5MkUOfreDOxgOGTJEK5mcuSSKM6Ny8Cjuvo4Jf2NnppT9aBGOaadtz5gxIzMzEz4ohJKSBD18+LDqiZsz/ByAf+/msRl0UA1aCN+oUaOKyP1TQe3atSm7SVTAcS0oOlVVhiLKOvSOseKudCsswjHt1JESZutVcOHCBeW/7DQ5A0mSoUaNGuPGjTMkbUrxIFWpbrQL2zJlypRXkMuaOnWqSiwoqx6goVz9RBEylXanhKE60sHEYyCIoOxHsXZ3ape1ZpdUe40pzow+wpKTkym7ELWyyRU4JuYg6IMHD46IiChSiR84cGBUVJRqVFNyaBwBpWh3lbRQxF3HaU3GirsyOrUI021O5+q0uiII6VTirnuFPgcSEhIiIyONmhGjJCIdbRRahz5buHChvnPfKdC4ceOkpCRHMikn63OMKmU3kw5nhrKwlN+Km6AMV8TiriOR5BRU4m7sCR/o5piYGGU07D7oVs/Awc/Pb+XKlYafEAQIDg5es2aN05qhFPmeAxBjBwg7/ZW/ptX0Yv+/8gmlE3UwwVjtrkRScJoXKLx27Zr7Taq2frq598IRYNkXLVrk5qoBR5zdeb1Vq1bp6em1atUy0KsJDQ3dsmWLqwP9ICX83gTPOftsKPSqBJHikepYuaTD/+GAki6L0OZevnzZfdEEAcoeouyZJwJEfMyYMcuXL6eYck1AUV2u/Hu2HjMsLOzgwYP9+/c3ROI7duy4c+dO2A1ObULPgTOrKFxDii5TaXeKuOvQQcQRIh9RgSYQVOAT3YFPywvAv9WqVatevfpLZPiBnas1qFoFSzVXpXt5vhJAD7zYadOmsQ2dhi9io4g7v2PYEozU1FRIKsYk5WwwpwCdmpiYiBrQlxwyWXP8pBlTXk4roegg1fAuokSkMH0CNGbMmNGsWTMYOnaJFTjDDpx5ageWC0ExiLcSAQ9fX19+1U5PA4ZVxTBo0KABmoQoz507l7/2RmUo9W2cYTsYYK+DgoIiIiKio6MbNWpk+FHxMlDW01JOCcZnv379gPCkSZMWL14s36RFqbxy5cp9+vSBoAcEBFB2HQhT/nl5eRBrp1VRpj9VL1I0QlFkqOrUqTNq1ChV13vYgf+ih/C2oKysLMedI/Pnz4eIs7OB0HnwiD744ANOJSojSzk3MDAwEFSxK/h8fHwwLP39/fEQnxjNTEUV6bJkiuqi+LsMTy8vr6SkpLFjxy5YsGDVqlXsTi+TQ0qXHUhYr1698PDwTp06tWjRgm1zIVIqvHTk7t27V65cAQ8dfxKujXUMVTXtLaSDUB0g/NWn5jyUno1TuHr1ak5OjnL/HlthrBwAwthCFfVT8rWbN2+GFldaXk1HTLoPlM1sdDQY02rUqDHNDufOnTt+/PiFCxfYjj50HsYwpLBmzZoY5Cwi1DGeKVed7d+/36m4C9fqAE9VRxfFAbomgnjoziJ4hISECAtt3769Vq1aHNYLOaXK6cKg8/dW4lc29/s/WWrPgHKQr9a1e7IE17GDo2p3E+eqVavyC6BF6BH4gY6MzcjIENavIzOjA4TV6k7dWKBB+fEWGLRhwwa+fREudVJ1w/379/kVsgPgX/0WKiUoV9K5AjdHo7kwuI+zU7WtgrVr16pGsmS/Onf9+vVCYlXOMXG/n9YAXTgFqf9YJbhBdevW5RfauXOnq6yCZL87Sngwk+qKNsrUlbGJeR3AD74ZuLkd1nCg7OVD4PTdd9/J7GVzICtWrIDLyn/R8UYtirjr6EehG6n7jBBbelK4BwejLTk52RXev/zyC3+PCPSW6ugOcJyvzIpuvaTJ9UXhygLQf5STz4pi3tQdgOYSZidA3bfffqu8zPrkyZNjxowR8vw1OyifFMVKEBNhFOkWdxtrWrZsmZKSwmcQ9MHAgQMR1yrFFM8R6U+ZMoU/gjGiQkNDlS9S0GUnRRl4/SpD8saNG9nZ2ceOHcvLy8vPz8dDBAn169ePj49XtnX06FGKY0q8fvWVAYYfomHhXl7EglFRUYmJiZGRkUeOHElISBBuSTHZ8w2qseTmMX2uQGgQrl27pk82bNi3bt0aRorfuwUFBbGxsRs3bgTNcjPgWlxcnPAsA1hY5dwt24spJAmVG6g7Fy1atGPHjsOHD+fm5jqep4c4oXv37t7e3jJpwhMvGBTKhNhJMpsYXWb7n0yj/ZdCIBczFf7iCJJJUdGLap+/bn7+6svkVZMmTShb1+FPjh8/HpqIvqAjMDBQJWH/K40AbwIWQM+RHib7+vewsDB+Ocl+E0vjxo2XLl2KsQXvCg49dAPlbpZOnTqp2ERJehib5Nq3bx+crrNnzzrdeIWHkydPlo07WInCwjpBVMOGDV+yCC6Y6THelkxPJRM+H9s/5b+nrv/w6yPXf7Z3TdITq3TvmXTFas2zWi9arZck6W+T9C8aNUmF8rOajmDQFEfWq1dP9YS+eU/TgTCUY5XY9dROAa4yBnxGRgYMl+pwUpt2xxCHacvMzOS3wU47GDJkCEu6QyYo1KJyx003lFDVWL+wadOmqampHNLmz58P1gwfPhyGaNasWZQNozDTISEhhXYkPphoffZfs1TMdrSy2cNshjYpZoZOkcDnQkEexNSulDE8IK9PbA9c8dKMH6y2AuYnJumZrOjx/1OPTsVLfWMyF6qZLYE2PNBHncprz+gwadIk6FNoYuYIsUvXWOajVKlS9+0AIzlx4kTZ46UcvNGmTRsEKp52QETB1g5A658/fx4+qqwrvby89uzZI1/P9NwV69u37xdffEFUqM/sQKQWKgE2QeXxU2ZwjLp7jUGDBg2EHITh+vHHH+V/hXUixC+0usNcrJh0zyz9a2GuhWQyCetQFhA6opL6P0m6aTWrRSMgIAA2h398nw6AvLZv316ldyneM2JiSv0Ilo4fP86GBGUB8A07CIvBE0lPT5fF/Xna2N/fv3PnzsYyiLED+tKRKaq9fE7B2N19kADKRYpOT1pzBTq2ihoNjx2HFMxpTEyM4VMWXbt2dVwQb+A+jNOnT8v5PWPzXUqV9FzcYT7GjBljOI98fX3ff/99x/ttKOPSWHNcokQJyhU3dMDgiY6OLkSXTfKKEVS6UQD/547Z5CR72L9/f2N3N7u6j8jAMFRp841FXonkS0Fs0aKF47HUbsLYsWOdbjigLBEz3PuEnTHwqu74+HgooUJOmvzx6sDi1Afy9vaGljGwGdhG+MqOslFESzyMFXdlqPoSXbaG2MDTuAMDAz/88EOn44dytazhM02IbCj3gFLAz89v3Lhxqod27X6f4IMbBYiByzodXpDCzz77zKjb1lFbYmKi43N2jKGB9MhT1MZu81UuBik0OoODg6dPn26IggePpk6d6nTwSPaLoQ2khwig6z//+Y87V2kzAFGpqalOIgFbcuXBqxN3m/UrrupBGTAgIfGGdGXv3r27devmWBWeGCvusu011ndXes5qrxr6b+DAge63ERsb6yqSQyuUJKPh2p0dAvPzzz+7ozygMpctWwbLbnLMS5g52cQiAclU0tZ/ZieNQt0gGAsPD3dT4uvXr79gwQKnK9iePn1qbLJYdl+NPYVXufZJrRvg0sybN69nz57usKl169bJycmuVvkRz1QqiiViwCc0NHTdunXQzToIrFq16vr166OiokyucnCvUNrNtilVlz4uuzlw+fLlderU0d2VsIQbN25U7X+T4c6dOwYuAC5Tpoys1F3tPdcHytXzanFHMzDWaWlpgwcP1tEkXkG8C5lglzQ5LcNmBIRVFVEYxK66T09PR19qaqJVq1YZGRnO9ToD2yKOV7c63zayzKXsLpTL+5YxPrdu3apasEQBcKZly5bgEufIVdXZQW5CxYoVmTPD0KZkjYngJBGpYhNC45SUlDlz5qiSD8J6R40atWbNGtW5AyogintRXGElE1ivXr39+/ePHDkSrQgJxNCF0799+/aAgAATd26Fo26Np8KGiXj3qr+/P6RW02kIMAuffvrptm3b2FGbrug11vwq90zDxXA/xJJBWRXvZuARI0YcPnw4JiZGuNPCw8OjefPmmzdvnjlzprAwAnDHCQv5EAX2LiSMsg9NN6AVjOTZs2f//vvvw4YNc+XbANX33nvv4MGDCQkJjC4OaVabD22bDrSyqNU+W4XvVskiSWbJarE9Yn82sLC1AVY4dyjG5rZYyRcTsrbvktlqMltZnZLyD69brFZmpgUbZcqXL4/YGmoerjxf6EFvr169oAimTp0q3CNr4BwTWunRo4eyLXdOVpJP40AM0KVLlwEDBrz8SXidED5zcnJ++umnLVu2HD169OHDh+wh8w4RynTs2BHurHxrilBZSvb71FeuXAknHnLv7e3t4+MDo/n666/Dpz9//jycwrp16wYFBb2C3UyMFrh3+/bty8rKys3NRfgFZgGxkJCQiIgINupotyk9e/ZoqenZ/5mlYhLcDHNFs7miZIE/CjMF1+41yVzied4G0mp6ZJUemaQnZjNE1rYUzGx9CAdBMv1jkp7a1saY75qku2bbiho4KzCGj9lGbsZiW07GXNzq0a5YySEW23cxz9mXP/74A67moUOH8vLybt68CYZDrKHFwXD4eN26dZO9FyHJ6D50Oio02UUfHgFUJBrCc7bMRD4DQ4mDXC1egReApmvWrAk+Dx06VB4/KHnixAlYVLwOkUXNiB88PT1L/H97Zx5S1fbF8ftej6BCG9QmUH6/skiKsLSsqGyw1CLLBoII1LKJLCqtjKIBKoMGorksozKJzKjERgisMDJ8ZmmT2qRZqYkWVv4R/j7dVdvTueq93vy9936/e9Yfl3v33Wedtdde+7vXOmfvtc0k2UckLYDJPBVAlDNCpI6rqyvdhzmpR7F1251tmZLUnnluX1JSUlFRIZaKj6XeCDTJNC1vKpc3+95Nu+WxQwYzEwFioFeW6XKxulyzBpjin+4oS3w1C3m/vyv9XV5d/fYd7rWvtPj5+7eJ4NsCnd9sf/qpa2m9q8ZtbLJsZMvPz8cPwSh1WeJMP1ZkSPoX+TT92AGI2YjVaq/SLrZrWv/ZLH/zL50zyKB/LP1t+/wNMuivJ8PcDXIgMszdIAciw9wNciAyzN0gByLD3A1yIDLM3SAHIsPcDXIgMszdIAciw9wNciBqzqPq/rHU0EKJehfq6M4FUcs/1OoOVWJZ7c2bN1VVVR4eHlYX9NW7rsMyk1ZDJZ8+fXr58mWHDh06duwo5fUuOGmciS0i2aJJHU9LXZmsrYdpRCpLwaRaSUnJhw8f3N3dZWeFLpOUYqgWBX1nYks2s/91yszM9PT07K6hHj165OXlyb+JiYn89DRT3759lyxZUl5erhLo8SU0NDQoKEhx+/r16+DBgyMiIrR1MPTAwMAWLVrQ005OTuvXr5fsfNeuXZM7Cn/5LifVWNLHjx99fHymTp0q6WC5vLCwsGfPnmvXrpWlhZTwZePGjc7OznJeMbKVlpYqSWrNGf9gMnfuXFUIW266YcMGrcAhISFKFf7+/ihBbto4wXzgwIHSHPk8fvy44pmenk7huHHj1I2Sk5OpQysUB2xUqwq+3Lp1S3FISEhQuqIvYmJi3r9/rxWAOs+fPw8ICJAT9tq0abNixQo57gr5p02b5u3tXV1dLTVramoGDRo0ZcoUyXgHOQS6u7q6Sk6Y+Pj4li1bhoWFySpw+ZcOKCgoWLZsGdUePHiwZ8+e3Nzcixcvqs01RUVFutQJaFx7YiGajYqKorPXrVvn5eWVkpICh1oz2HTt2lX27O7btw8wllw3DeUIaNWqlZ+fH0LCv1u3bnBISkrCVjBNlUdu165djCWG35w5c+7evRsXF4c1X7p0SS2IxQi4XHfCHEx0uX2Ki4u/fPmC2NjE2bNn0YnkY2pck8LcxcUlODi41gyc2sOOYFhoJvCFhpjM2RHRrW6vJiW+vr5Dhw5VvaP+oi+4PDo6msLs7Ozt27c/efJE8pBK62A4YcKEFy9eoIRevXpduHABxSK8HEID4qB8+g4T5+fVq1eRZP/+/XWJi/9u5P3rCKNEKSCfFgsh9IUeHj16JAiBGaHZx48fqwr0DReqn9Tp3LkzBqf4VFZWotDIyEgBSHpXAEZ7a6bdkSNHWhUyJycHw6UvBcgBOWYSBe1YA2OmT58+CsDAP6TFIJQwyEAdRogW3WngokWLtOjev39/UQVUVlaGBY8aNcqqeDB3c3NjVOh0KDyvXLkidokqhPPRo0cp0SpTMqQuX768Xg47d+7k34cPH8q9sGO0IUmbpcK2bdsYcgcOHBBVoxlJTa5aini0XRbcg3GogjuqWxih6k8kO1nRC86J7VdVVFSg97q8m3/8YfdmHEx52LBhJ0+ehOGNGzeePXsGACtXGLhiaM2bN0/xX7hwYa3ZZ/iVJmPrTEpWT96zkQCC06dPN37ChY2C4YpgtSqVAOaO09WpU6dZs2aJThgM+DxKP6hlxowZly9ffvv2LV5QWlpaeHi4No2HYe51JAAAlsi5kLZfCNijU1wC2epV+wtbCOi5+fPnY+UZGRnHjh3r0qWLNitEVlYWXa5OW6H8X2aymr258VYztHDYMCP7NKYrxBY/f/7MiLWbg+lHPCq5RNXBXuA0XuKQIUMAFO2F6jsKWbBgQa357B38QHx6YhgtW4fw3a2SuKSo6c6dO3v37gVfrZ5XpSXZy0wsOHbsWGZbgjnTL2zFkoMVdu/eTZiLB6LNIAdomX7exQzhPdd79q1VYnDiZhCW4M4RPeNB2XghEQ5RinxfvHixLm8PsS+h5JEjR5h5GuJAeCockDk2Nla37ZW+MJlT8h88eHD06NGEMVLO/MPIVNsL8/Pz5aAAxgOF8nyGu+OVEUDTp2PGjNEdVmWY+zfCViQBMjqaOHEiptYk0+Eq+szZ2RmLJwIDnrdu3Wp3JgX6HoDctGkTsTLgpN3eJjGf7vwzld28qeael5cnER4OLk6zdgtz4/Tq1SvgUwAVUS3TVBH/TJ48GZtuiAMBqDxyoS0ghe7f8ePHyxeifARTwCFZnJRiV61axYzKF5S/efNmKRSA5+70aWpqqk4nhjPzjdDRzZs3iej5DpzIka5NIqZXcA6wJIw7dOiQxJp2CxMREcEn4KRLxyAoqHtMRHyGxduR7RWv9/Xr1zi7DJUmZebAHGnp06dPsVpJRqIjsIM5Jz4+viEOs2fPFg5MFJa4kJ6enpKSIvCsPbNRnmipTH07duwgvLEc5MHBweA9quNy3V+GuX+jWvM597gihJt0kjY5Fn+hUK3tyiMCy8T+kJubW0JCwoABA7B4uzNsSeTATekz3V1w003mxBBaSQoKCrA57XscvmsFbiirDyMEY4qMjMSATp06ZV8WrYYSlmDQ586dKy0ttYMDamSOZcAcPnxYq0YUAmfGifz08PDo3bt3rcV2ftrVtm1bOtTyGFDD3OsITYWFhYE3uMLachcXl7KyMpUPkTisvLy83jRX8uoH3x3Ebd50oUJ4SmDe9evXVUlOTg4OvfYYGWRo3769ePlCxcXFXIUN1Suwv78/zjGudjMehiWaxFITExPti2EQeObMmZmZmdnZ2aoQdCdOvX37trjsdnA2zL2OUB9zOtgAwGvR0cfHB0dTHipDZ86c4V+JR4VqamqSkpLEXOjjrKwsUPO/ceIcZk0oRgBH0CZPVHDxmQe02dxpBdMLgzY3N1cefsuxan5+fvXah5jmvXv3CNObUVTi6UmTJqnXbXaQ9IU8uVeiEsxUV1evWbNGOqjWtuMmqfanmYzXTPrXTPidTk5O4Le6St4jAplLly6Niopq3bq1u7u7JNuROmAYaAT0rl69GscR+9uyZYv2nbztr5mEmBzoaXlZo2tCWloanjpGT6AWGBgokZmYtaqWkZFBHeb6lStXhoeH4/Fj64xJLR/ta6bCwkLmfQIGq4LJayZ8reHDhw8zEw604imIwMiREnWWuuVrJrQhHPhMTk5WHNRrJhEsICCgXbt2XKJaRyumT59Oq0eMGBETExMUFCTJ7LXNl4729vbWHqWI2ABQC9sfP/0fEHrEI0RHWkjAv0ShoaGhOHyoBr1Q8m8zSQU0zox///798+fP80knnThxQp58SQUvLy/GA8Fuamoqrk5sbGx0dLRKACgE3FKNONgWObEq6vuZSYdenp6evr6+TOh4xsSsjMC4uDjdvYjS+vXrB1ojD45+SEgIGCnLbLSqIBJgwJjMCaYrKysZwNTUsdIRBoQGcIslbRhEo5hM5N+qqiqmHZhIUkRUVFRUhDDoVs11zEhwYPbjWmHC5eqx77t373ACmRZkiQeCgTvdu3dn6KolEvAHj2gdoxo7ZpSibV0K3ry8PDw0gQOTeVogqkbU/wBlRkVoHF/CdAAAAABJRU5ErkJggg== \ No newline at end of file diff --git a/inegration_test.go b/inegration_test.go index 75cc272..e541d30 100644 --- a/inegration_test.go +++ b/inegration_test.go @@ -17,5 +17,5 @@ func TestApp(t *testing.T) { func schema(t *testing.T) { t.Parallel() - assert.SchemaExists(t, "template", []string{ /* insert tables */ }) + assert.SchemaExists(t, "gp_joule", []string{"configuration", "asset"}) } diff --git a/main.go b/main.go index 409c64a..c776538 100644 --- a/main.go +++ b/main.go @@ -41,9 +41,6 @@ func main() { boil.DebugWriter = log.GetWriter(log.TraceLevel, "database") } - // Necessary to close used init resources, because db.Pool() is used in this app. - defer db.ClosePool() - // Initialize the app initialization() diff --git a/metadata.json b/metadata.json index 4812511..f3585ac 100644 --- a/metadata.json +++ b/metadata.json @@ -1,20 +1,20 @@ { - "name": "template", + "name": "gp-joule", "elionaMinVersion": "v11.0.0", "displayName": { - "en": "App template", - "de": "App Vorlage" + "en": "GP Joule", + "de": "GP Joule" }, "description": { - "en": "This template can be used to create an app stub for an Eliona environment.", - "de": "Diese Vorlage kann verwendet werden um eine App für Eliona zu erstellen." + "en": "The GP Joule app allows access to GP Joule stations.", + "de": "Die GP Joule App ermöglicht den Zugriff auf GP Joule Stationen." }, "dashboardTemplateNames": [ - "Template" + "GP Joule" ], "apiUrl": "v1", "apiSpecificationPath": "/version/openapi.json", - "documentationUrl": "https://doc.eliona.io/eliona/referenzen/app-entwicklung", + "documentationUrl": "https://doc.eliona.io/", "useEnvironment": [ "CONNECTION_STRING", "INIT_CONNECTION_STRING", diff --git a/model/model.go b/model/model.go index 6d5d5ee..70ef0fe 100644 --- a/model/model.go +++ b/model/model.go @@ -18,8 +18,8 @@ package model import ( "context" "fmt" - "template/apiserver" - "template/conf" + "gp-joule/apiserver" + "gp-joule/conf" "github.com/eliona-smart-building-assistant/go-eliona/asset" "github.com/eliona-smart-building-assistant/go-eliona/utils" @@ -91,15 +91,15 @@ type Root struct { } func (r *Root) GetName() string { - return "template" + return "gp_joule" } func (r *Root) GetDescription() string { - return "Root asset for template devices" + return "Root asset for GP Joule" } func (r *Root) GetAssetType() string { - return "template_root" + return "gp_joule_root" } func (r *Root) GetGAI() string { diff --git a/openapi.yaml b/openapi.yaml index fd82b3e..d4f020b 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -17,29 +17,36 @@ openapi: 3.0.3 info: version: 1.0.0 - title: App template API - description: API to access and configure the app template + title: GP Joule app API + description: API to access and configure the GP Joule app externalDocs: - description: Find out more about the app template - url: https://github.com/eliona-smart-building-assistant/app-template + description: Find out more about the GP Joule app + url: https://github.com/eliona-smart-building-assistant/gp-joule-app servers: - - url: http://template/v1 + - url: "https://{server}/v1" + variables: + server: + default: loriot-io + - url: "https://{environment}.eliona.io/apps/loriot-io/api/v1" + variables: + environment: + default: name tags: - name: Configuration description: Configure the app externalDocs: - url: https://github.com/eliona-smart-building-assistant/app-template + url: https://github.com/eliona-smart-building-assistant/gp-joule-app - name: Version description: API version externalDocs: - url: https://github.com/eliona-smart-building-assistant/app-template + url: https://github.com/eliona-smart-building-assistant/gp-joule-app - name: Customization description: Help to customize Eliona environment externalDocs: - url: https://github.com/eliona-smart-building-assistant/app-template + url: https://github.com/eliona-smart-building-assistant/gp-joule-app paths: /configs: @@ -176,7 +183,7 @@ paths: required: true schema: type: string - example: Template + example: GP Joule - name: projectId in: query description: Define the project the dashboard should be diff --git a/reset.sql b/reset.sql index e50b885..a9515f2 100644 --- a/reset.sql +++ b/reset.sql @@ -20,51 +20,51 @@ SET SCHEMA 'public'; DELETE FROM versioning.patches -WHERE app_name = 'template'; +WHERE app_name = 'gp-joule'; INSERT INTO public.eliona_store (app_name, category, version) -VALUES ('template', 'app', '1.0.0') +VALUES ('gp-joule', 'app', '1.0.0') ON CONFLICT (app_name) DO UPDATE SET version = '1.0.0'; INSERT INTO public.eliona_app (app_name, enable) -VALUES ('template', 't') +VALUES ('gp-joule', 't') ON CONFLICT (app_name) DO UPDATE SET initialized_at = null; -DROP SCHEMA IF EXISTS template CASCADE; +DROP SCHEMA IF EXISTS gp_joule CASCADE; DELETE FROM heap WHERE asset_id IN ( SELECT asset_id FROM asset - WHERE asset_type LIKE E'template\\_%' + WHERE asset_type LIKE E'gp\\_joule\\_%' ); DELETE FROM attribute_schema -WHERE asset_type LIKE E'template\\_%'; +WHERE asset_type LIKE E'gp\\_joule\\_%'; DELETE FROM asset -WHERE asset_type LIKE E'template\\_%'; +WHERE asset_type LIKE E'gp\\_joule\\_%'; DELETE FROM asset_type -WHERE asset_type LIKE E'template\\_%'; +WHERE asset_type LIKE E'gp\\_joule\\_%'; DELETE FROM public.widget_data WHERE widget_id IN ( SELECT public.widget.id FROM public.widget JOIN public.dashboard USING (dashboard_id) - WHERE public.dashboard.name LIKE 'Template%' + WHERE public.dashboard.name LIKE 'GP Joule%' ); DELETE FROM public.widget WHERE dashboard_id IN ( SELECT dashboard_id FROM public.dashboard - WHERE name LIKE 'Template%' + WHERE name LIKE 'GP Joule%' ); DELETE FROM public.dashboard -WHERE name LIKE 'Template%'; +WHERE name LIKE 'GP Joule%'; --- DELETE FROM eliona_app WHERE app_name = 'template'; --- DELETE FROM eliona_store WHERE app_name = 'template'; +-- DELETE FROM eliona_app WHERE app_name = 'gp-joule'; +-- DELETE FROM eliona_store WHERE app_name = 'gp-joule'; diff --git a/sqlboiler.toml b/sqlboiler.toml index 0e4855d..91588f5 100644 --- a/sqlboiler.toml +++ b/sqlboiler.toml @@ -11,7 +11,7 @@ host = "localhost" port = 60001 user = "postgres" pass = "secret" -schema = "template" +schema = "gp_joule" sslmode = "disable" [[types]]