Skip to content

Commit 36374eb

Browse files
committed
Add initial user API
1 parent cb6cae4 commit 36374eb

File tree

12 files changed

+997
-0
lines changed

12 files changed

+997
-0
lines changed
Binary file not shown.

backend/dbscripts/thunderdb/postgress.sql

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
-- Table to store Users
2+
CREATE TABLE USER (
3+
ID INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
4+
USER_ID VARCHAR(36) UNIQUE NOT NULL,
5+
ORG_ID VARCHAR(36) NOT NULL,
6+
TYPE VARCHAR(50) NOT NULL,
7+
ATTRIBUTES JSONB,
8+
CREATED_AT TIMESTAMPTZ DEFAULT NOW(),
9+
UPDATED_AT TIMESTAMPTZ DEFAULT NOW()
10+
);
11+
112
-- Table to store basic service provider (app) details.
213
CREATE TABLE SP_APP (
314
ID INT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
@@ -102,3 +113,8 @@ VALUES
102113
('550e8400-e29b-41d4-a716-446655440000', '550e8400-e29b-41d4-a716-446655440001', '550e8400-e29b-41d4-a716-446655440003'),
103114
('550e8400-e29b-41d4-a716-446655440000', '550e8400-e29b-41d4-a716-446655440001', '550e8400-e29b-41d4-a716-446655440004'),
104115
('550e8400-e29b-41d4-a716-446655440000', '550e8400-e29b-41d4-a716-446655440002', '550e8400-e29b-41d4-a716-446655440005');
116+
117+
INSERT INTO USER (USER_ID, ORG_ID, TYPE, ATTRIBUTES, CREATED_AT, UPDATED_AT)
118+
VALUES
119+
('550e8400-e29b-41d4-a716-446655440000', '456e8400-e29b-41d4-a716-446655440001', 'person',
120+
'{"age": 30, "roles": ["admin", "user"], "address": {"city": "Colombo", "zip": "00100"}}');

backend/dbscripts/thunderdb/sqlite.sql

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,14 @@
1+
-- Table to store Users
2+
CREATE TABLE USER (
3+
ID INTEGER PRIMARY KEY AUTOINCREMENT,
4+
USER_ID VARCHAR(36) UNIQUE NOT NULL,
5+
ORG_ID VARCHAR(36) NOT NULL,
6+
TYPE TEXT NOT NULL,
7+
ATTRIBUTES TEXT,
8+
CREATED_AT TEXT DEFAULT (datetime('now')),
9+
UPDATED_AT TEXT DEFAULT (datetime('now'))
10+
);
11+
112
-- Table to store basic service provider (app) details.
213
CREATE TABLE SP_APP (
314
ID INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -102,3 +113,10 @@ VALUES
102113
('550e8400-e29b-41d4-a716-446655440000', '550e8400-e29b-41d4-a716-446655440001', '550e8400-e29b-41d4-a716-446655440003'),
103114
('550e8400-e29b-41d4-a716-446655440000', '550e8400-e29b-41d4-a716-446655440001', '550e8400-e29b-41d4-a716-446655440004'),
104115
('550e8400-e29b-41d4-a716-446655440000', '550e8400-e29b-41d4-a716-446655440002', '550e8400-e29b-41d4-a716-446655440005');
116+
117+
INSERT INTO USER (USER_ID, ORG_ID, TYPE, ATTRIBUTES, CREATED_AT, UPDATED_AT)
118+
VALUES (
119+
'550e8400-e29b-41d4-a716-446655440000', '456e8400-e29b-41d4-a716-446655440001', 'person',
120+
'{"age": 30, "roles": ["admin", "user"], "address": {"city": "Colombo", "zip": "00100"}}',
121+
datetime('now'), datetime('now')
122+
);

backend/internal/system/managers/servicemanager.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ func (sm *ServiceManager) RegisterServices() error {
4343

4444
// Register the token service.
4545
services.NewTokenService(sm.mux)
46+
47+
// Register the User service.
48+
services.NewUserService(sm.mux)
4649
// Register the Application service.
4750
services.NewApplicationService(sm.mux)
4851

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
/*
2+
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
package services
20+
21+
import (
22+
"github.com/asgardeo/thunder/internal/user/handler"
23+
"net/http"
24+
)
25+
26+
type UserService struct {
27+
userHandler *handler.UserHandler
28+
}
29+
30+
func NewUserService(mux *http.ServeMux) *UserService {
31+
32+
instance := &UserService{
33+
userHandler: handler.NewUserHandler(),
34+
}
35+
instance.RegisterRoutes(mux)
36+
37+
return instance
38+
}
39+
40+
func (s *UserService) RegisterRoutes(mux *http.ServeMux) {
41+
42+
mux.HandleFunc("POST /users", s.userHandler.HandleUserPostRequest)
43+
mux.HandleFunc("GET /users", s.userHandler.HandleUserListRequest)
44+
mux.HandleFunc("GET /users/", s.userHandler.HandleUserGetRequest)
45+
mux.HandleFunc("PUT /users/", s.userHandler.HandleUserPutRequest)
46+
mux.HandleFunc("DELETE /users/", s.userHandler.HandleUserDeleteRequest)
47+
}
Lines changed: 258 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
/*
2+
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
package handler
20+
21+
import (
22+
"encoding/json"
23+
"github.com/asgardeo/thunder/internal/system/log"
24+
"github.com/asgardeo/thunder/internal/user/model"
25+
userprovider "github.com/asgardeo/thunder/internal/user/provider"
26+
"net/http"
27+
"strings"
28+
"sync"
29+
)
30+
31+
// @title User Management API
32+
// @version 1.0
33+
// @description This API is used to manage users.
34+
//
35+
// @license.name Apache 2.0
36+
// @license.url http://www.apache.org/licenses/LICENSE-2.0.html
37+
//
38+
// @host localhost:8090
39+
// @BasePath /
40+
type UserHandler struct {
41+
store map[string]model.User
42+
mu *sync.RWMutex
43+
}
44+
45+
func NewUserHandler() *UserHandler {
46+
47+
return &UserHandler{
48+
store: make(map[string]model.User),
49+
mu: &sync.RWMutex{},
50+
}
51+
}
52+
53+
// HandleUserPostRequest handles the user request.
54+
//
55+
// @Summary Create an user
56+
// @Description Creates a new user with the provided details.
57+
// @Tags users
58+
// @Accept json
59+
// @Produce json
60+
// @Param user body model.User true "User data"
61+
// @Success 201 {object} model.User
62+
// @Failure 400 {string} "Bad Request: The request body is malformed or contains invalid data."
63+
// @Failure 500 {string} "Internal Server Error: An unexpected error occurred while processing the request."
64+
// @Router /users [post]
65+
func (ah *UserHandler) HandleUserPostRequest(w http.ResponseWriter, r *http.Request) {
66+
67+
logger := log.GetLogger().With(log.String(log.LOGGER_KEY_COMPONENT_NAME, "UserHandler"))
68+
69+
var userInCreationRequest model.User
70+
if err := json.NewDecoder(r.Body).Decode(&userInCreationRequest); err != nil {
71+
http.Error(w, "Invalid request body", http.StatusBadRequest)
72+
return
73+
}
74+
75+
// Create the user using the user service.
76+
userProvider := userprovider.NewUserProvider()
77+
userService := userProvider.GetUserService()
78+
createdUser, err := userService.CreateUser(&userInCreationRequest)
79+
if err != nil {
80+
http.Error(w, "Failed to create user", http.StatusInternalServerError)
81+
return
82+
}
83+
84+
w.Header().Set("Content-Type", "user/json")
85+
w.WriteHeader(http.StatusCreated)
86+
87+
err = json.NewEncoder(w).Encode(createdUser)
88+
if err != nil {
89+
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
90+
return
91+
}
92+
93+
// Log the user creation response.
94+
logger.Debug("User POST response sent", log.String("user id", createdUser.Id))
95+
}
96+
97+
// HandleUserListRequest handles the user request.
98+
//
99+
// @Summary List users
100+
// @Description Retrieve a list of all users.
101+
// @Tags users
102+
// @Accept json
103+
// @Produce json
104+
// @Success 200 {array} model.User
105+
// @Failure 400 {string} "Bad Request: The request body is malformed or contains invalid data."
106+
// @Failure 500 {string} "Internal Server Error: An unexpected error occurred while processing the request."
107+
// @Router /users [get]
108+
func (ah *UserHandler) HandleUserListRequest(w http.ResponseWriter, r *http.Request) {
109+
110+
logger := log.GetLogger().With(log.String(log.LOGGER_KEY_COMPONENT_NAME, "UserHandler"))
111+
112+
// Get the user list using the user service.
113+
userProvider := userprovider.NewUserProvider()
114+
userService := userProvider.GetUserService()
115+
users, err := userService.GetUserList()
116+
if err != nil {
117+
http.Error(w, "Failed get user list", http.StatusInternalServerError)
118+
return
119+
}
120+
121+
w.Header().Set("Content-Type", "user/json")
122+
err = json.NewEncoder(w).Encode(users)
123+
if err != nil {
124+
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
125+
return
126+
}
127+
128+
// Log the user response.
129+
logger.Debug("User GET (list) response sent")
130+
}
131+
132+
// HandleUserGetRequest handles the user request.
133+
//
134+
// @Summary Get an user by ID
135+
// @Description Retrieve a specific user using its ID.
136+
// @Tags users
137+
// @Accept json
138+
// @Produce json
139+
// @Param id path string true "User ID"
140+
// @Success 200 {object} model.User
141+
// @Failure 400 {string} "Bad Request: The request body is malformed or contains invalid data."
142+
// @Failure 404 {string} "Not Found: The user with the specified ID does not exist."
143+
// @Failure 500 {string} "Internal Server Error: An unexpected error occurred while processing the request."
144+
// @Router /users/{id} [get]
145+
func (ah *UserHandler) HandleUserGetRequest(w http.ResponseWriter, r *http.Request) {
146+
147+
logger := log.GetLogger().With(log.String(log.LOGGER_KEY_COMPONENT_NAME, "UserHandler"))
148+
149+
id := strings.TrimPrefix(r.URL.Path, "/users/")
150+
if id == "" {
151+
http.Error(w, "Missing user id", http.StatusBadRequest)
152+
return
153+
}
154+
155+
// Get the user using the user service.
156+
userProvider := userprovider.NewUserProvider()
157+
userService := userProvider.GetUserService()
158+
user, err := userService.GetUser(id)
159+
if err != nil {
160+
http.Error(w, "Failed get user", http.StatusInternalServerError)
161+
return
162+
}
163+
164+
w.Header().Set("Content-Type", "user/json")
165+
err = json.NewEncoder(w).Encode(user)
166+
if err != nil {
167+
http.Error(w, "Failed to encode response", http.StatusInternalServerError)
168+
return
169+
}
170+
171+
// Log the user response.
172+
logger.Debug("User GET response sent", log.String("user id", id))
173+
}
174+
175+
// HandleUserPutRequest handles the user request.
176+
//
177+
// @Summary Update an user
178+
// @Description Update the details of an existing user.
179+
// @Tags users
180+
// @Accept json
181+
// @Produce json
182+
// @Param id path string true "User ID"
183+
// @Param user body model.User true "Updated user data"
184+
// @Success 200 {object} model.User
185+
// @Failure 400 {string} "Bad Request: The request body is malformed or contains invalid data."
186+
// @Failure 404 {string} "Not Found: The user with the specified ID does not exist."
187+
// @Failure 500 {string} "Internal Server Error: An unexpected error occurred while processing the request."
188+
// @Router /users/{id} [put]
189+
func (ah *UserHandler) HandleUserPutRequest(w http.ResponseWriter, r *http.Request) {
190+
191+
logger := log.GetLogger().With(log.String(log.LOGGER_KEY_COMPONENT_NAME, "UserHandler"))
192+
193+
id := strings.TrimPrefix(r.URL.Path, "/users/")
194+
if id == "" {
195+
http.Error(w, "Missing user id", http.StatusBadRequest)
196+
return
197+
}
198+
199+
var updatedUser model.User
200+
if err := json.NewDecoder(r.Body).Decode(&updatedUser); err != nil {
201+
http.Error(w, "Invalid request body", http.StatusBadRequest)
202+
return
203+
}
204+
updatedUser.Id = id
205+
206+
// Update the user using the user service.
207+
userProvider := userprovider.NewUserProvider()
208+
userService := userProvider.GetUserService()
209+
user, err := userService.UpdateUser(id, &updatedUser)
210+
if err != nil {
211+
http.Error(w, "Failed get user", http.StatusInternalServerError)
212+
return
213+
}
214+
215+
w.Header().Set("Content-Type", "user/json")
216+
json.NewEncoder(w).Encode(user)
217+
218+
// Log the user response.
219+
logger.Debug("User PUT response sent", log.String("user id", id))
220+
}
221+
222+
// HandleUserDeleteRequest handles the user request.
223+
//
224+
// @Summary Delete an user
225+
// @Description Delete an user using its ID.
226+
// @Tags users
227+
// @Accept json
228+
// @Produce json
229+
// @Param id path string true "User ID"
230+
// @Success 204
231+
// @Failure 400 {string} "Bad Request: The request body is malformed or contains invalid data."
232+
// @Failure 404 {string} "Not Found: The user with the specified ID does not exist."
233+
// @Failure 500 {string} "Internal Server Error: An unexpected error occurred while processing the request."
234+
// @Router /users/{id} [delete]
235+
func (ah *UserHandler) HandleUserDeleteRequest(w http.ResponseWriter, r *http.Request) {
236+
237+
logger := log.GetLogger().With(log.String(log.LOGGER_KEY_COMPONENT_NAME, "UserHandler"))
238+
239+
id := strings.TrimPrefix(r.URL.Path, "/users/")
240+
if id == "" {
241+
http.Error(w, "Missing user id", http.StatusBadRequest)
242+
return
243+
}
244+
245+
// Delete the user using the user service.
246+
userProvider := userprovider.NewUserProvider()
247+
userService := userProvider.GetUserService()
248+
err := userService.DeleteUser(id)
249+
if err != nil {
250+
http.Error(w, "Failed delete user", http.StatusInternalServerError)
251+
return
252+
}
253+
254+
w.WriteHeader(http.StatusNoContent)
255+
256+
// Log the user response.
257+
logger.Debug("User DELETE response sent", log.String("user id", id))
258+
}

backend/internal/user/model/user.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2025, WSO2 LLC. (http://www.wso2.com).
3+
*
4+
* WSO2 LLC. licenses this file to you under the Apache License,
5+
* Version 2.0 (the "License"); you may not use this file except
6+
* in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing,
12+
* software distributed under the License is distributed on an
13+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
* KIND, either express or implied. See the License for the
15+
* specific language governing permissions and limitations
16+
* under the License.
17+
*/
18+
19+
package model
20+
21+
import "encoding/json"
22+
23+
type User struct {
24+
Id string `json:"id,omitempty"`
25+
OrgId string `json:"org_id,omitempty"`
26+
Type string `json:"type,omitempty"`
27+
Attributes json.RawMessage `json:"attributes,omitempty"`
28+
}

0 commit comments

Comments
 (0)