Suppose I have the following tables in my database (which I can redesign at will if I want to):
user: info about users of the system each user has a set of
authorization.
authorization: info about what each authorization means. each
authorization is associated with a number of hosts it applies to.
host: info about hosts involved in the application. each host has a
location.
areas: groups locations together.
location: group hosts together.
I want to design a webservice interface to this database schemas so that
clients of this webservice will be able to ask questions such as:
what are the users of this system?
what authorizations has user X?
in what location is host Y?
what are the hosts located in location Z?
-. .... and so on.
What should the functions/methods of this interface be?
One popular solution is to have:
getuser(id)/getusers() get users, along with the list
of ids of authorizations each user have.
getauth(id)/getauths() get all authorizations along with the
list of ids of hosts they apply to.
gethost(id)/gethosts() get all the hosts. along with the id of
the location of each host.
.....
With this scheme, getting the location of a hosts means doing two calls
to the api:
h = gethost(id),
loc_id = h.location_id;
location = getlocation(loc_id);
What I don't like about this approach is that it forces the client of
the api to do a lot of (asynchronous) calls to the API tracing through
the hierarchy of objects before getting the info he wants.
In your experience is this the best way to do this?
PS. If your solution involves changing the schema, I'm all ears too.
EDIT: In other words, what I want is good middle ground between
Solution 1: Web service has only one method: query(String) which accepts a query in some db query language like SQL.
Solution 2: Web service has one method for every possible query that users might want: getUserWithAuthorization(user_id) getUser(user_id) getLocationOfHost(host_id) getAreasWithLocations(), ... etc. #interfacebloatinghell.
First of all, drop all verbs (getResourceX).
Usually you can design the API in the way that a client MAY do a single call and getting all the information but also get data on a more granular level.
If I assume correctly your set of data in order from largest to smallest subsets is like this:
Areas -> Locations -> Hosts -> Authorizations -> Users
So this can be your resource URI
1 get call to
api/areas
will return all Areas along with all their Locations and for each Location their hosts etc.
If you want information about a particular Area you can do 1 call to
/api/areas/{id}
Related
In my database, I have two tables, User and Location. Each user should have a location. If I have a user and want to update it, I use PUT on "users/:id", and could add locationId if I want to. What if location does not already exist? I am aware that I could first create a location and then update with the new locationId, but I would like to be able to do it in one request.
I have implemented a way to do this, where I can send a location-object under key: "location" in the body when doing the request in "users/:id". This works, but I understand that this means I am creating in my database, when the requests in itself is a PUT-request. I this allowed in REST, or do I need to do two requests to follow the best practices of REST? The two requests seems pretty annoying for client if I have multiple associations to the user, and would be a lot of requests.
This is my first question, so I hope I don't miss a thing. To be clear from the start: I don't expect an answer which dives deep into detail. This is just about getting a general understanding of how to work with this kind of software.
So I don't know if "Identity Management System" is a suitable term for what I mean but when I talk about Identity Management Systems I think of something like Azure AD, which as far as I know provides e.g. web developers the possibility to integrate a way users can authenticate (including access privilege etc.) on their website.
What I'm quite unsure about is how to work with/ integrate such tools in a project. I will try to make it clear with an example: Assuming I have a website let's say this website is a blog. The blog consist of different posts which are stored in my own database which is connected to the website. The posts are written by different users which authenticate with a tool like Azure AD. The user's data is stored somewhere on a server run by e.g. Microsoft. If I want to display the posts togethere with the name, email.... of the user who wrote them, how would I do this?
Is it possible to query the user's data directly from the Identity Management System and display it? This does not sound ideal to me as the consequence would be that data the website uses is stored in two different locations.
Would you kind of copy the user's data from the Identity Management System to the websites database and query it from there? This does not sound like a good solution either because then data would be duplicated.
So whats the "right workflow"?
I appreciate any hints and further information I can get:-)
AFAIK To get the user's information like name, email etc. you can add these claims while generating the JWT token.
To generate access token, you have multiple authentication flows such as Authorization code flow, ROPC flow, Implicit flow.
To add the claims that you need to return with the token, you can make settings like below:
Go to Azure Portal -> Azure Active Directory -> App Registrations -> Your app -> Token configuration -> Add optional claims
When you decode the token via JSON Web Tokens - jwt.io you can find the user information that you need.
To know how to generate access token, you can refer SO Thread which I solved it before.
We are a using a micro-service based pattern for our project where we have Users and their Orders. Users personal information (name, email, mobile) is stored in User table in relational database while we are storing Orders data of users in Orders collection in NoSql database. We want to develop an API to get a paginated list of all the orders placed with order details along with finer details of user associated like - user name, mobile, email along with each order. We are storing userId in Orders collection.
The problem is how do we get User details for each order in this list since both the resources are in different databases. We also thought of storing user name, email and mobile in Orders collection only but what if a user updates their profile, the Orders collection will have stale user data.
What is the best approach to address this issue?
You can use API gateway pattern, UI will call to API gateway endpoint and the Endpoint will call the both the API/services to get the result and aggregate it then returns aggregated response to the UI (caller)
https://microservices.io/patterns/apigateway.html
Well it mostly depends on scalability needs in terms of data size and number of requests. You may go with the API gateway if you don't have too much data and you don't get many requests to that service.
Otherwise if you really need something scalable then you should implement your own thought with an event based communication.
I already provided an answer for a similar situation you can take a look
https://stackoverflow.com/a/63957775/3719412
You have two services Orders and Users. You are requesting Orders service to get all Orders. It will return a response data which will contains ID of Users (each Order contains ID of User). Then, you will make a request to a Users service to get an information regarding User by ID which you got before. And finally, you can aggregate those results (if it is needed).
As guys mention, good solution will be to implement API Gateway here. As a client, you will send a request to a single port with endpoint (to a Gateway) and Gateway should create logic which I have described before.
We now have one site running but we will need to build a branded site for our client soon. The client site will have exactly the same data set as our current site expect for the user data. The client site must have totally separated user info which allows only the client to use the site.
I don't see the need for setting up a new database or creating a new user table for the client. My tentative solution is add a "Company" column for the user table so that I can differ which site the user data row is on.
I do not know if this approach will work or not or if it is the best practice. Could anyone with experience like this shed some light on this question?
Thanks,
Nigong
P.S. I use LAMP with AWS.
Using an extra column to store a company / entity id is a common approach for multitenant system. In general you will want to abstract the part that that verifies you can only retrieve data you're allowed to a piece that all queries go through, like your ORM. This will prevent people new to the project from exposing/using data that shouldn't be exposed/used.
I have followed most of the tutorials on the luminusweb.net website, setting up a database system using the +h2 new app. What I have currently mirrors the guestbook setup like the tutorial shows. I am now wondering how I can access specific entries into the migrations up table. More specifically, I am trying to have restricted access to webpages (a login system) based on the entries inside of the table.
The migration file purpose is to create the tables in your database. To access those tables you will have to write queries in the file located here: your_project > resources > sql > queries.sql
Here you should write queries, there are a few examples on the Luminus website. When you see parameters with semicolumns, it means that you have to pass a map with those parameters when you call these queries in your program. Ex: if you have this query:
-- name: accounts_for_user
-- retrieve all accounts a user has access to and the associated rights
SELECT account_name, admin
FROM accounts_users
WHERE email = :email;
The call:
(db/accounts_for_user {:email "laurent#test.com"})
will return a lazy sequence like this:
[{"account_name":"account1","admin":false},
{"account_name":"account2","admin":true},
{"account_name":"account2","admin":true}]
Then if you want to restrict the access to a specific page based on what's in your database, there are a few options. The Buddy auth library offers a few options, the easiest to use is the session one. First, when a user enters a correct password, you inject their identifier in :session :identity in any request. For instance
(-> (redirect "/accounts-list")
(assoc :session {:identity "user#test.com"}))
The identity parameter will be in every request until the session dies (30 minutes by default) or you overwrite it. In your pages, you can test buddy.auth/authenticated? on the requests, and redirect to an error page or whatever you like if it returns false. I am currently writing a tutorial for webapps using Luminus, I'll update this answer when it's available.