diff --git a/scnserver/api/handler/apiClient.go b/scnserver/api/handler/apiClient.go index 72c299f..86161dd 100644 --- a/scnserver/api/handler/apiClient.go +++ b/scnserver/api/handler/apiClient.go @@ -119,11 +119,11 @@ func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse { UserID models.UserID `uri:"uid" binding:"entityid"` } type body struct { - FCMToken string `json:"fcm_token" binding:"required"` - AgentModel string `json:"agent_model" binding:"required"` - AgentVersion string `json:"agent_version" binding:"required"` - DescriptionName *string `json:"description_name"` - ClientType models.ClientType `json:"client_type" binding:"required"` + FCMToken string `json:"fcm_token" binding:"required"` + AgentModel string `json:"agent_model" binding:"required"` + AgentVersion string `json:"agent_version" binding:"required"` + Name *string `json:"name"` + ClientType models.ClientType `json:"client_type" binding:"required"` } var u uri @@ -148,7 +148,7 @@ func (h APIHandler) AddClient(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to delete existing clients in db", err) } - client, err := h.database.CreateClient(ctx, u.UserID, clientType, b.FCMToken, b.AgentModel, b.AgentVersion, b.DescriptionName) + client, err := h.database.CreateClient(ctx, u.UserID, clientType, b.FCMToken, b.AgentModel, b.AgentVersion, b.Name) if err != nil { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create client in db", err) } @@ -231,10 +231,10 @@ func (h APIHandler) UpdateClient(g *gin.Context) ginresp.HTTPResponse { ClientID models.ClientID `uri:"cid" binding:"entityid"` } type body struct { - FCMToken *string `json:"fcm_token"` - AgentModel *string `json:"agent_model"` - AgentVersion *string `json:"agent_version"` - DescriptionName *string `json:"description_name"` + FCMToken *string `json:"fcm_token"` + AgentModel *string `json:"agent_model"` + AgentVersion *string `json:"agent_version"` + Name *string `json:"name"` } var u uri @@ -284,14 +284,14 @@ func (h APIHandler) UpdateClient(g *gin.Context) ginresp.HTTPResponse { } } - if b.DescriptionName != nil { - if *b.DescriptionName == "" { + if b.Name != nil { + if *b.Name == "" { err = h.database.UpdateClientDescriptionName(ctx, u.ClientID, nil) if err != nil { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update client", err) } } else { - err = h.database.UpdateClientDescriptionName(ctx, u.ClientID, langext.Ptr(*b.DescriptionName)) + err = h.database.UpdateClientDescriptionName(ctx, u.ClientID, langext.Ptr(*b.Name)) if err != nil { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to update client", err) } diff --git a/scnserver/api/handler/apiUser.go b/scnserver/api/handler/apiUser.go index ef12927..30eccc1 100644 --- a/scnserver/api/handler/apiUser.go +++ b/scnserver/api/handler/apiUser.go @@ -28,14 +28,14 @@ import ( // @Router /api/v2/users [POST] func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse { type body struct { - FCMToken string `json:"fcm_token"` - ProToken *string `json:"pro_token"` - Username *string `json:"username"` - AgentModel string `json:"agent_model"` - AgentVersion string `json:"agent_version"` - DescriptionName *string `json:"description_name"` - ClientType models.ClientType `json:"client_type"` - NoClient bool `json:"no_client"` + FCMToken string `json:"fcm_token"` + ProToken *string `json:"pro_token"` + Username *string `json:"username"` + AgentModel string `json:"agent_model"` + AgentVersion string `json:"agent_version"` + ClientName *string `json:"client_name"` + ClientType models.ClientType `json:"client_type"` + NoClient bool `json:"no_client"` } var b body @@ -124,7 +124,7 @@ func (h APIHandler) CreateUser(g *gin.Context) ginresp.HTTPResponse { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to delete existing clients in db", err) } - client, err := h.database.CreateClient(ctx, userobj.UserID, clientType, b.FCMToken, b.AgentModel, b.AgentVersion, b.DescriptionName) + client, err := h.database.CreateClient(ctx, userobj.UserID, clientType, b.FCMToken, b.AgentModel, b.AgentVersion, b.ClientName) if err != nil { return ginresp.APIError(g, 500, apierr.DATABASE_ERROR, "Failed to create client in db", err) } diff --git a/scnserver/db/impl/primary/clients.go b/scnserver/db/impl/primary/clients.go index 922f815..d324e68 100644 --- a/scnserver/db/impl/primary/clients.go +++ b/scnserver/db/impl/primary/clients.go @@ -7,7 +7,7 @@ import ( "time" ) -func (db *Database) CreateClient(ctx db.TxContext, userid models.UserID, ctype models.ClientType, fcmToken string, agentModel string, agentVersion string, descriptionName *string) (models.Client, error) { +func (db *Database) CreateClient(ctx db.TxContext, userid models.UserID, ctype models.ClientType, fcmToken string, agentModel string, agentVersion string, name *string) (models.Client, error) { tx, err := ctx.GetOrCreateTransaction(db) if err != nil { return models.Client{}, err @@ -21,7 +21,7 @@ func (db *Database) CreateClient(ctx db.TxContext, userid models.UserID, ctype m TimestampCreated: time2DB(time.Now()), AgentModel: agentModel, AgentVersion: agentVersion, - DescriptionName: descriptionName, + Name: name, } _, err = sq.InsertSingle(ctx, tx, "clients", entity) @@ -172,7 +172,7 @@ func (db *Database) UpdateClientDescriptionName(ctx db.TxContext, clientid model return err } - _, err = tx.Exec(ctx, "UPDATE clients SET description_name = :vvv WHERE client_id = :cid", sq.PP{ + _, err = tx.Exec(ctx, "UPDATE clients SET name = :vvv WHERE client_id = :cid", sq.PP{ "vvv": descriptionName, "cid": clientid, }) diff --git a/scnserver/db/schema/assets.go b/scnserver/db/schema/assets.go index ceef776..77c2f59 100644 --- a/scnserver/db/schema/assets.go +++ b/scnserver/db/schema/assets.go @@ -40,7 +40,7 @@ var PrimarySchema = map[int]Def{ 2: {primarySchema2, "07ed1449114416ed043084a30e0722a5f97bf172161338d2f7106a8dfd387d0a"}, 3: {primarySchema3, "65c2125ad0e12d02490cf2275f0067ef3c62a8522edf9a35ee8aa3f3c09b12e8"}, 4: {primarySchema4, "cb022156ab0e7aea39dd0c985428c43cae7d60e41ca8e9e5a84c774b3019d2ca"}, - 5: {primarySchema5, "04bd0d4a81540f69f10c8f8cd656a1fdf852d4ef7a2ab2918ca6369b5423b1b6"}, + 5: {primarySchema5, "9d6217ba4a3503cfe090f72569367f95a413bb14e9effe49ffeabbf255bce8dd"}, } var PrimarySchemaVersion = 5 diff --git a/scnserver/db/schema/primary_5.ddl b/scnserver/db/schema/primary_5.ddl index bed6725..35a2101 100644 --- a/scnserver/db/schema/primary_5.ddl +++ b/scnserver/db/schema/primary_5.ddl @@ -46,17 +46,17 @@ CREATE UNIQUE INDEX "idx_keytokens_token" ON keytokens (token); CREATE TABLE clients ( - client_id TEXT NOT NULL, + client_id TEXT NOT NULL, - user_id TEXT NOT NULL, - type TEXT CHECK(type IN ('ANDROID', 'IOS')) NOT NULL, - fcm_token TEXT NOT NULL, - description_name TEXT NULL, + user_id TEXT NOT NULL, + type TEXT CHECK(type IN ('ANDROID','IOS','LINUX','MACOS','WINDOWS')) NOT NULL, + fcm_token TEXT NOT NULL, + name TEXT NULL, - timestamp_created INTEGER NOT NULL, + timestamp_created INTEGER NOT NULL, - agent_model TEXT NOT NULL, - agent_version TEXT NOT NULL, + agent_model TEXT NOT NULL, + agent_version TEXT NOT NULL, PRIMARY KEY (client_id) ) STRICT; diff --git a/scnserver/db/schema/primary_migration_4_5.ddl b/scnserver/db/schema/primary_migration_4_5.ddl index 658d440..efa9540 100644 --- a/scnserver/db/schema/primary_migration_4_5.ddl +++ b/scnserver/db/schema/primary_migration_4_5.ddl @@ -1,6 +1,32 @@ -ALTER TABLE clients ADD COLUMN "description_name" TEXT NULL; +ALTER TABLE clients ADD COLUMN "name" TEXT NULL; + +DROP INDEX "idx_clients_userid"; +DROP INDEX "idx_clients_fcmtoken"; + +CREATE TABLE clients_new +( + client_id TEXT NOT NULL, + + user_id TEXT NOT NULL, + type TEXT CHECK(type IN ('ANDROID','IOS','LINUX','MACOS','WINDOWS')) NOT NULL, + fcm_token TEXT NOT NULL, + name TEXT NULL, + + timestamp_created INTEGER NOT NULL, + + agent_model TEXT NOT NULL, + agent_version TEXT NOT NULL, + + PRIMARY KEY (client_id) +) STRICT; + +INSERT INTO clients_new SELECT * FROM clients; +DROP TABLE clients; +ALTER TABLE clients_new RENAME TO clients; +CREATE INDEX "idx_clients_userid" ON clients (user_id); +CREATE UNIQUE INDEX "idx_clients_fcmtoken" ON clients (fcm_token); diff --git a/scnserver/models/client.go b/scnserver/models/client.go index ba0f4a6..69a7c15 100644 --- a/scnserver/models/client.go +++ b/scnserver/models/client.go @@ -26,7 +26,7 @@ type Client struct { TimestampCreated time.Time AgentModel string AgentVersion string - DescriptionName *string + Name *string } func (c Client) JSON() ClientJSON { @@ -38,7 +38,7 @@ func (c Client) JSON() ClientJSON { TimestampCreated: c.TimestampCreated.Format(time.RFC3339Nano), AgentModel: c.AgentModel, AgentVersion: c.AgentVersion, - DescriptionName: c.DescriptionName, + Name: c.Name, } } @@ -50,7 +50,7 @@ type ClientJSON struct { TimestampCreated string `json:"timestamp_created"` AgentModel string `json:"agent_model"` AgentVersion string `json:"agent_version"` - DescriptionName *string `json:"description_name"` + Name *string `json:"name"` } type ClientDB struct { @@ -61,7 +61,7 @@ type ClientDB struct { TimestampCreated int64 `db:"timestamp_created"` AgentModel string `db:"agent_model"` AgentVersion string `db:"agent_version"` - DescriptionName *string `db:"description_name"` + Name *string `db:"name"` } func (c ClientDB) Model() Client { @@ -73,7 +73,7 @@ func (c ClientDB) Model() Client { TimestampCreated: timeFromMilli(c.TimestampCreated), AgentModel: c.AgentModel, AgentVersion: c.AgentVersion, - DescriptionName: c.DescriptionName, + Name: c.Name, } } diff --git a/scnserver/models/enums_gen.go b/scnserver/models/enums_gen.go index 6048a26..d2f9863 100644 --- a/scnserver/models/enums_gen.go +++ b/scnserver/models/enums_gen.go @@ -5,7 +5,7 @@ package models import "gogs.mikescher.com/BlackForestBytes/goext/langext" import "gogs.mikescher.com/BlackForestBytes/goext/enums" -const ChecksumEnumGenerator = "814434d6d179eec7fb90b380db859daf6051b5d767d83a20633f9ef78d66e857" // GoExtVersion: 0.0.463 +const ChecksumEnumGenerator = "08404cad6879118878e2b64db01bb6bf54cda9d51e3d3ff91b239e9a8459fecd" // GoExtVersion: 0.0.463 // ================================ ClientType ================================ // diff --git a/scnserver/models/ids_gen.go b/scnserver/models/ids_gen.go index 775b9d7..6b9512a 100644 --- a/scnserver/models/ids_gen.go +++ b/scnserver/models/ids_gen.go @@ -15,7 +15,7 @@ import "reflect" import "regexp" import "strings" -const ChecksumCharsetIDGenerator = "814434d6d179eec7fb90b380db859daf6051b5d767d83a20633f9ef78d66e857" // GoExtVersion: 0.0.463 +const ChecksumCharsetIDGenerator = "08404cad6879118878e2b64db01bb6bf54cda9d51e3d3ff91b239e9a8459fecd" // GoExtVersion: 0.0.463 const idlen = 24 diff --git a/scnserver/swagger/swagger.json b/scnserver/swagger/swagger.json index 62684b6..af8eac7 100644 --- a/scnserver/swagger/swagger.json +++ b/scnserver/swagger/swagger.json @@ -1981,6 +1981,64 @@ } } }, + "/api/v2/users/{uid}/keys/current": { + "get": { + "description": "Can be done with keys of any permission - the returned key does not include its token.", + "tags": [ + "API-v2" + ], + "summary": "Get the key currently used by this request", + "operationId": "api-tokenkeys-get-current", + "parameters": [ + { + "type": "string", + "description": "UserID", + "name": "uid", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "TokenKeyID", + "name": "kid", + "in": "path", + "required": true + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "$ref": "#/definitions/models.KeyTokenWithTokenJSON" + } + }, + "400": { + "description": "supplied values/parameters cannot be parsed / are invalid", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + }, + "401": { + "description": "user is not authorized / has missing permissions", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + }, + "404": { + "description": "message not found", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + }, + "500": { + "description": "internal server error", + "schema": { + "$ref": "#/definitions/ginresp.apiError" + } + } + } + } + }, "/api/v2/users/{uid}/keys/{kid}": { "get": { "description": "The request must be done with an ADMIN key, the returned key does not include its token.", @@ -3039,10 +3097,10 @@ "client_type": { "$ref": "#/definitions/models.ClientType" }, - "description_name": { + "fcm_token": { "type": "string" }, - "fcm_token": { + "name": { "type": "string" } } @@ -3084,12 +3142,12 @@ "agent_version": { "type": "string" }, + "client_name": { + "type": "string" + }, "client_type": { "$ref": "#/definitions/models.ClientType" }, - "description_name": { - "type": "string" - }, "fcm_token": { "type": "string" }, @@ -3641,10 +3699,10 @@ "client_id": { "type": "string" }, - "description_name": { + "fcm_token": { "type": "string" }, - "fcm_token": { + "name": { "type": "string" }, "timestamp_created": { @@ -3736,6 +3794,44 @@ } } }, + "models.KeyTokenWithTokenJSON": { + "type": "object", + "properties": { + "all_channels": { + "type": "boolean" + }, + "channels": { + "type": "array", + "items": { + "type": "string" + } + }, + "keytoken_id": { + "type": "string" + }, + "messages_sent": { + "type": "integer" + }, + "name": { + "type": "string" + }, + "owner_user_id": { + "type": "string" + }, + "permissions": { + "type": "string" + }, + "timestamp_created": { + "type": "string" + }, + "timestamp_lastused": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, "models.MessageJSON": { "type": "object", "properties": { diff --git a/scnserver/swagger/swagger.yaml b/scnserver/swagger/swagger.yaml index 37d7ba3..ca74f8d 100644 --- a/scnserver/swagger/swagger.yaml +++ b/scnserver/swagger/swagger.yaml @@ -129,10 +129,10 @@ definitions: type: string client_type: $ref: '#/definitions/models.ClientType' - description_name: - type: string fcm_token: type: string + name: + type: string required: - agent_model - agent_version @@ -163,10 +163,10 @@ definitions: type: string agent_version: type: string + client_name: + type: string client_type: $ref: '#/definitions/models.ClientType' - description_name: - type: string fcm_token: type: string no_client: @@ -529,10 +529,10 @@ definitions: type: string client_id: type: string - description_name: - type: string fcm_token: type: string + name: + type: string timestamp_created: type: string type: @@ -594,6 +594,31 @@ definitions: timestamp_lastused: type: string type: object + models.KeyTokenWithTokenJSON: + properties: + all_channels: + type: boolean + channels: + items: + type: string + type: array + keytoken_id: + type: string + messages_sent: + type: integer + name: + type: string + owner_user_id: + type: string + permissions: + type: string + timestamp_created: + type: string + timestamp_lastused: + type: string + token: + type: string + type: object models.MessageJSON: properties: channel_id: @@ -2197,6 +2222,46 @@ paths: summary: Update a key tags: - API-v2 + /api/v2/users/{uid}/keys/current: + get: + description: Can be done with keys of any permission - the returned key does + not include its token. + operationId: api-tokenkeys-get-current + parameters: + - description: UserID + in: path + name: uid + required: true + type: string + - description: TokenKeyID + in: path + name: kid + required: true + type: string + responses: + "200": + description: OK + schema: + $ref: '#/definitions/models.KeyTokenWithTokenJSON' + "400": + description: supplied values/parameters cannot be parsed / are invalid + schema: + $ref: '#/definitions/ginresp.apiError' + "401": + description: user is not authorized / has missing permissions + schema: + $ref: '#/definitions/ginresp.apiError' + "404": + description: message not found + schema: + $ref: '#/definitions/ginresp.apiError' + "500": + description: internal server error + schema: + $ref: '#/definitions/ginresp.apiError' + summary: Get the key currently used by this request + tags: + - API-v2 /api/v2/users/{uid}/subscriptions: get: description: |- diff --git a/scnserver/test/client_test.go b/scnserver/test/client_test.go index c87e7bf..d2f17a6 100644 --- a/scnserver/test/client_test.go +++ b/scnserver/test/client_test.go @@ -49,6 +49,7 @@ func TestGetClient(t *testing.T) { tt.AssertEqual(t, "fcm_token", "DUMMY_FCM", c0["fcm_token"]) tt.AssertEqual(t, "client_type", "ANDROID", c0["type"]) tt.AssertEqual(t, "user_id", uid, fmt.Sprintf("%v", c0["user_id"])) + tt.AssertEqual(t, "client_name", nil, c0["name"]) cid := fmt.Sprintf("%v", c0["client_id"]) @@ -77,19 +78,52 @@ func TestCreateClient(t *testing.T) { fmt.Printf("uid := %s\n", uid) fmt.Printf("admin_key := %s\n", admintok) - tt.RequestAuthPost[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients", gin.H{ - "agent_model": "DUMMY_PHONE_2", - "agent_version": "99X", - "client_type": "IOS", - "fcm_token": "DUMMY_FCM_2", - }) - type rt3 struct { Clients []gin.H `json:"clients"` } - r3 := tt.RequestAuthGet[rt3](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients") - tt.AssertEqual(t, "len(clients)", 2, len(r3.Clients)) + // normal client + { + createdClient := tt.RequestAuthPost[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients", gin.H{ + "agent_model": "DUMMY_PHONE_2", + "agent_version": "99X", + "client_type": "IOS", + "fcm_token": "DUMMY_FCM_2", + }) + + tt.AssertEqual(t, "createdClient.agent_model", "DUMMY_PHONE_2", createdClient["agent_model"]) + tt.AssertEqual(t, "createdClient.agent_version", "99X", createdClient["agent_version"]) + tt.AssertEqual(t, "createdClient.type", "IOS", createdClient["type"]) + tt.AssertEqual(t, "createdClient.fcm_token", "DUMMY_FCM_2", createdClient["fcm_token"]) + tt.AssertEqual(t, "createdClient.name", nil, createdClient["name"]) + } + + { + r3 := tt.RequestAuthGet[rt3](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients") + tt.AssertEqual(t, "len(clients)", 2, len(r3.Clients)) + } + + // client with name + { + createdClient := tt.RequestAuthPost[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients", gin.H{ + "agent_model": "DUMMY_PHONE_2_ASDF", + "agent_version": "XOXO", + "client_type": "LINUX", + "fcm_token": "DUMMY_FCM_2_77777", + "name": "My iPhone 12 Pro Max", + }) + + tt.AssertEqual(t, "createdClient.agent_model", "DUMMY_PHONE_2_ASDF", createdClient["agent_model"]) + tt.AssertEqual(t, "createdClient.agent_version", "XOXO", createdClient["agent_version"]) + tt.AssertEqual(t, "createdClient.type", "LINUX", createdClient["type"]) + tt.AssertEqual(t, "createdClient.fcm_token", "DUMMY_FCM_2_77777", createdClient["fcm_token"]) + tt.AssertEqual(t, "createdClient.name", "My iPhone 12 Pro Max", createdClient["name"]) + } + + { + r3 := tt.RequestAuthGet[rt3](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients") + tt.AssertEqual(t, "len(clients)", 3, len(r3.Clients)) + } } func TestDeleteClient(t *testing.T) { @@ -256,33 +290,81 @@ func TestUpdateClient(t *testing.T) { cid2 := fmt.Sprintf("%v", r2["client_id"]) - type rt3 struct { - Clients []gin.H `json:"clients"` + { + type rt3 struct { + Clients []gin.H `json:"clients"` + } + + r3 := tt.RequestAuthGet[rt3](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients") + tt.AssertEqual(t, "len(clients)", 2, len(r3.Clients)) } - r3 := tt.RequestAuthGet[rt3](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients") - tt.AssertEqual(t, "len(clients)", 2, len(r3.Clients)) + { + r4 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients/"+cid2) + tt.AssertEqual(t, "agent_model", "DUMMY_PHONE_2", r4["agent_model"]) + tt.AssertEqual(t, "agent_version", "99X", r4["agent_version"]) + tt.AssertEqual(t, "client_type", "IOS", r4["type"]) + tt.AssertEqual(t, "fcm_token", "DUMMY_FCM_2", r4["fcm_token"]) + } - r4 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients/"+cid2) - tt.AssertEqual(t, "agent_model", "DUMMY_PHONE_2", r4["agent_model"]) - tt.AssertEqual(t, "agent_version", "99X", r4["agent_version"]) - tt.AssertEqual(t, "client_type", "IOS", r4["type"]) - tt.AssertEqual(t, "fcm_token", "DUMMY_FCM_2", r4["fcm_token"]) + { + r5 := tt.RequestAuthPatch[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients/"+cid2, gin.H{ + "agent_model": "PP_DUMMY_PHONE_2", + "agent_version": "PP_99X", + "fcm_token": "PP_DUMMY_FCM_2", + }) + tt.AssertEqual(t, "agent_model", "PP_DUMMY_PHONE_2", r5["agent_model"]) + tt.AssertEqual(t, "agent_version", "PP_99X", r5["agent_version"]) + tt.AssertEqual(t, "client_type", "IOS", r5["type"]) + tt.AssertEqual(t, "fcm_token", "PP_DUMMY_FCM_2", r5["fcm_token"]) + } - r5 := tt.RequestAuthPatch[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients/"+cid2, gin.H{ - "agent_model": "PP_DUMMY_PHONE_2", - "agent_version": "PP_99X", - "fcm_token": "PP_DUMMY_FCM_2", - }) - tt.AssertEqual(t, "agent_model", "PP_DUMMY_PHONE_2", r5["agent_model"]) - tt.AssertEqual(t, "agent_version", "PP_99X", r5["agent_version"]) - tt.AssertEqual(t, "client_type", "IOS", r5["type"]) - tt.AssertEqual(t, "fcm_token", "PP_DUMMY_FCM_2", r5["fcm_token"]) + { + r6 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients/"+cid2) + tt.AssertEqual(t, "agent_model", "PP_DUMMY_PHONE_2", r6["agent_model"]) + tt.AssertEqual(t, "agent_version", "PP_99X", r6["agent_version"]) + tt.AssertEqual(t, "client_type", "IOS", r6["type"]) + tt.AssertEqual(t, "fcm_token", "PP_DUMMY_FCM_2", r6["fcm_token"]) + } - r6 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients/"+cid2) - tt.AssertEqual(t, "agent_model", "PP_DUMMY_PHONE_2", r6["agent_model"]) - tt.AssertEqual(t, "agent_version", "PP_99X", r6["agent_version"]) - tt.AssertEqual(t, "client_type", "IOS", r6["type"]) - tt.AssertEqual(t, "fcm_token", "PP_DUMMY_FCM_2", r6["fcm_token"]) + { + r7 := tt.RequestAuthPatch[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients/"+cid2, gin.H{ + "name": "I am the name", + }) + tt.AssertEqual(t, "agent_model", "PP_DUMMY_PHONE_2", r7["agent_model"]) + tt.AssertEqual(t, "agent_version", "PP_99X", r7["agent_version"]) + tt.AssertEqual(t, "client_type", "IOS", r7["type"]) + tt.AssertEqual(t, "fcm_token", "PP_DUMMY_FCM_2", r7["fcm_token"]) + tt.AssertEqual(t, "name", "I am the name", r7["name"]) + } + + { + r8 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients/"+cid2) + tt.AssertEqual(t, "agent_model", "PP_DUMMY_PHONE_2", r8["agent_model"]) + tt.AssertEqual(t, "agent_version", "PP_99X", r8["agent_version"]) + tt.AssertEqual(t, "client_type", "IOS", r8["type"]) + tt.AssertEqual(t, "fcm_token", "PP_DUMMY_FCM_2", r8["fcm_token"]) + tt.AssertEqual(t, "name", "I am the name", r8["name"]) + } + + { + r9 := tt.RequestAuthPatch[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients/"+cid2, gin.H{ + "name": "", + }) + tt.AssertEqual(t, "agent_model", "PP_DUMMY_PHONE_2", r9["agent_model"]) + tt.AssertEqual(t, "agent_version", "PP_99X", r9["agent_version"]) + tt.AssertEqual(t, "client_type", "IOS", r9["type"]) + tt.AssertEqual(t, "fcm_token", "PP_DUMMY_FCM_2", r9["fcm_token"]) + tt.AssertEqual(t, "name", nil, r9["name"]) + } + + { + r10 := tt.RequestAuthGet[gin.H](t, admintok, baseUrl, "/api/v2/users/"+uid+"/clients/"+cid2) + tt.AssertEqual(t, "agent_model", "PP_DUMMY_PHONE_2", r10["agent_model"]) + tt.AssertEqual(t, "agent_version", "PP_99X", r10["agent_version"]) + tt.AssertEqual(t, "client_type", "IOS", r10["type"]) + tt.AssertEqual(t, "fcm_token", "PP_DUMMY_FCM_2", r10["fcm_token"]) + tt.AssertEqual(t, "name", nil, r10["name"]) + } }