I'm using Laravel for a site where most database objects can be private (i.e., viewed only by their owner) or public (viewed by everyone, including guests). Each of these has a user_id, which I set to NULL when the object is public.
What's the simplest way of authenticating routes for this scenario? For example, in /routes/web.php I have:
Route::get('/{tournament}/players', [TournamentController::class, 'indexPlayers']);
and I want to make sure that tournament->user_id is either NULL or corresponds to the user's id. I was able to do this by explicitly binding tournament in /app/Providers/RouteServiceProvider.php:
Route::bind('tournament', function ($hash) {
$user_id = Auth::user()->id ?? NULL;
return Tournament::where([['hash', $hash], ['user_id', $user_id]])
->orWhere([['hash', $hash], ['user_id', NULL]])
->firstOrFail();
});
but I have the strong feeling that I'm making it too complicated or doing things in the wrong place. Is there a better way? Should I by doing this inside TournamentController, for example?
First, there is now the syntax Auth::id() that can be used as a shorthand for Auth::user()->id ?? NULL, so that saves some trouble.
Next, I ended up moving the logic out of RouteServiceProvider.php and into the controller, so that I can explicitly control what happens for public vs. private objects:
class TournamentController extends Controller
{
public function indexPlayers(Tournament $tournament)
{
if ($tournament->user_id === NULL) {
// public
} else if ($tournament->user_id === Auth::id()) {
// private
} else {
// unauthorized
}
}
...
}
Team
I'm building a spring boot application that can support multiple DBs either Cassandra, CouchDB or DynamoDB based on the configuration in application.yml.
My entity class has annotations that are specific to Cassandra and the annotations for DynamoDB are different. For eg. DynamoDB has #DynamoDBTable for Table and Cassandra has #org.springframework.data.cassandra.core.mapping.Table annotations.
The problem is that I would like to use a single entity object irrespective of the DB type because the entity is referred from multiple places in the application. What is the best design pattern to implement this?
In case of Cassandra -
package com.abc;
#Table("Cart")
public class Cart {
#PrimaryKeyColumn(ordinal = 0, type = PrimaryKeyType.PARTITIONED)
#GeneratedValue(strategy = GenerationType.AUTO)
protected String id;
#PrimaryKeyColumn(ordinal = 1, type = PrimaryKeyType.PARTITIONED)
private String userId;
#PrimaryKeyColumn(ordinal = 2, type = PrimaryKeyType.CLUSTERED, ordering = Ordering.DESCENDING)
private String skuId;
In case of DynamoDB -
#DynamoDBTable(tableName = "Cart")
public class Cart {
#DynamoDBHashKey
#DynamoDBAutoGeneratedKey
protected String id;
private String userId;
private String skuId;
Thanks
AA
I would suggest you to create an intermediary object which can act as a bridge between your application logic and database ORM.
You can create a helper function which populate those fields.
class CartDAO {
private String id;
private String userId;
private String skuId;
// Getters & Setters
}
class CartService{
CartDAO fetchFromDynamoDB(String Id)
{
// Fetch from DB
// Create CartDAO from that object
// Return CartDAO
}
CartDAO fetchFromCassandra(String Id)
{
// Fetch from DB
// Create CartDAO from that object
// Return CartDAO
}
}
Now you can use CartDAO seamlessly in your application logic.
Yes it is possible.
Option 1:
Simply put the required annotation of both MongoDB and Cassandra.
Each annotation will have there own package and definition. So provide the required definition.
Option 2:
As defined by snk01, you can use that approach as well.
Here i am assuming that you are writing the persistence layer for each database seperately.
Working with database-first approach creating ASPNETCORE MVC web app with user authentication, I would like to override the way the parameters from IdentityUser class are queried to the database. The reason is the current implementation of IdentityUser has two new parameters NormalizedEmail and NormalizedUserName (which in my opinion retracts from Normalization).
Is there a way I can write the code below in the Model class so that those two parameters are not included in the query to the database or is that something that needs to be done in the controller class?
public class IdentityUser : Microsoft.AspNetCore.Identity.EntityFrameworkCore.IdentityUser
{
public override string NormalizedUserName
{ get { return null; } set { value = null; } }
public override string NormalizedEmail
{ get { return null; } set { value = null; } }
}
Not far as I can tell, both parameters are part of the data model and as explained in this Issue #351
About Identity 3.0:
...Instead we compute a normalized representation of the user name and we
store it in a separate column so that lookups by normalized user name
should now be sargable.
So in other words, if you "override the way the parameters from IdentityUser class are queried to the database" in essence you'll be doing exactly the opposite the class intends to do.
I have a non nullable fields in a table with default values set already in the property "Default Value or Binding" in SSMS. I linked this table in an ASP.Net mvc application. When I created the view and when running the create view, it still asking me to enter the required fields for the non nullable fields even though i assigned a default value for them.
After this I removed the line:
#Html.ValidationMessageFor(model => model.position, "", new { #class = "text-danger" })
which is bellow each
#Html.EditorFor statement, but this time it post me back to the same page with no changes in the database.
How can I get rid of the message in the required fields as I have already default value for them?
Simply you can create a constructor in your model. It will initialize default values once new instance is created. If user provides that fields, then it will be overridden. If no, value from constructor will be passed to EF.
What you are trying to do now, won't work according to specifications: https://msdn.microsoft.com/en-us/library/ms187872.aspx
Here you can see how to achieve what you want, considering that this field will always be generate in database.
This is one of the many reasons not to use entity models as viewmodels. MVC doesn't care that the required field has a default value, that information is database-related and not related to input validation.
Introduce a viewmodel where those properties are not required, and map the posted viewmodel to your entity model.
So, given an entity model that looks like this:
public class SomeEntity
{
// this property is not-nullable in the database
[Required]
public string SomeRequiredDatabaseField { get; set; }
}
Because the SomeRequiredDatabaseField is NOT NULL, it is annotated as such by Entity Framework, even if it has a default value. MVC will pick up this annotation, and consider the model not valid when the property has no value.
Now if you introduce a viewmodel, you can tell MVC that this property is not required:
public class SomeViewModel
{
// Not required
public string SomeRequiredDatabaseField { get; set; }
}
Now in your controller, you map the viewmodel to the entity model (preferably using AutoMapper):
public ActionResult Post(SomeViewModel model)
{
if (ModelState.IsValid)
{
var entityToSave = new SomeEntity
{
SomeRequiredDatabaseField = model.SomeRequiredDatabaseField
};
db.SomeEntity.Add(entityToSave);
}
// ...
}
Hello all I am working on a small report library SPA using angular and breeze that allows a user to manage reports they create. I know you can use breeze along with the EF context to build the model by referencing the API.. But how this is being implemented I want to leave EF out of it. The API (WebAPI 2) is basically calling other repositories to do the work almost like an interface. The result coming back is just a json object. I have looked over the Edmunds sample from the Breeze website and I can see how I can build a client model as well as handle the mapping on the return. The current issue I have is I am not certain that I have the mapping in the jsonResultsAdapter correct. Or that I may be missing something on how the mapping is supposed to work.
I'm testing this currently with some mock data I have stubbed into the API until I can get this to work. Once this is bound and mapping I can go against the actual data. Here is what I have so far:
The mock data is a report object that contains an internal collection called labels (tags basically) a report can have multiple tag and from the label it can have multiple reports.
//report dto
public class ReportDto
{
public Int64 ReportId { get; set; }
public string ReportName { get; set; }
public string ReportDescription { get; set; }
public string ReportDateCreated { get; set; }
public string ReportOwner { get; set; }
public IEnumerable<ReportLabelDto> ReportLabels { get; set; }
}
public class ReportLabelDto
{
public Int64 LabelId { get; set; }
public string LabelName { get; set; }
public bool IsPrivate { get; set; }
public bool IsFavorite { get; set; }
public IEnumerable<ReportDto> Reports { get; set; }//placeholder?
}
Here is the code currently being used within the webapi controller which at this time is just for testing:
[Route ("reportlibrary/myreports/{userid}")]
public IEnumerable<ReportDto> GetAllReports(string userId)
{
List<ReportDto> result = new List<ReportDto>();
List<ReportLabelDto> label = new List<ReportLabelDto>();
//create 5 reports
for (int i = 0; i < 5; i++)
{
ReportDto r = new ReportDto();
ReportLabelDto l = new ReportLabelDto();
r.ReportId = i;
r.ReportOwner = "John Smith";
r.ReportDateCreated = DateTime.Now.ToString();
r.ReportDescription = "Report Description # " + i.ToString();
r.ReportName = "Report Description # " + i.ToString();
//generate labels
l.LabelId = i;
l.LabelName = "Special Label" + i.ToString();
l.IsPrivate = true;
l.IsFavorite = false;
label.Add(l);
r.ReportLabels = label;
result.Add(r);
}
return result;
}
The object that is currently coming back looks like this:
[{"ReportId":0,"ReportName":"Report Description # 0","ReportDescription":"Report Description # 0","ReportDateCreated":"12/22/2014 6:32:05 PM","ReportOwner":"John Smith","ReportLabels":[{"LabelId":0,"LabelName":"Special Label0","IsPrivate":true,"IsFavorite":false,"Reports":null},{"LabelId":1,"LabelName":"Special Label1","IsPrivate":true,"IsFavorite":false,"Reports":null},{"LabelId":2,"LabelName":"Special Label2","IsPrivate":true,"IsFavorite":false,"Reports":null},{"LabelId":3,"LabelName":"Special Label3","IsPrivate":true,"IsFavorite":false,"Reports":null},{"LabelId":4,"LabelName":"Special Label4","IsPrivate":true,"IsFavorite":false,"Reports":null}]},{"ReportId":1,"ReportName":"Report Description # 1","ReportDescription":"Report Description # 1","ReportDateCreated":"12/22/2014 6:32:05 PM","ReportOwner":"John Smith","ReportLabels":[{"LabelId":0,"LabelName":"Special Label0","IsPrivate":true,"IsFavorite":false,"Reports":null},{"LabelId":1,"LabelName":"Special Label1","IsPrivate":true,"IsFavorite":false,"Reports":null},{"LabelId":2,"LabelName":"Special Label2","IsPrivate":true,"IsFavorite":false,"Reports":null},{"LabelId":3,"LabelName":"Special Label3","IsPrivate":true,"IsFavorite":false,"Reports":null},{"LabelId":4,"LabelName":"Special Label4","IsPrivate":true,"IsFavorite":false,"Reports":null}]},...]
I have the services and controllers all talking and I can hit the api and get an object returned so I am going to omit that code for right now.
For the js model I defined the report object as follows:
app.factory('model', function () {
var DT = breeze.DataType;
return {
initialize: initialize
}
function initialize(metadataStore) {
metadataStore.addEntityType({
shortName: "Report",
namespace: "Inform",
dataProperties: {
reportid: { dataType: DT.Int64, isPartOfKey: true },
reportname: { dataType: DT.String },
reportdescription: { dataType: DT.String },
reportdatecreated: { dataType: DT.String },
reportowner: { dataType: DT.String },
mappedlabels: { dataType: DT.Undefined },
ishared: { dataType: DT.Bool },
isfavorite: { dataType: DT.Bool }
},
navigationProperties: {
labels: {
entityTypeName: "Label:#Inform", isScalar: false,
associationName: "Report_Labels"
}
}
});
metadataStore.addEntityType({
shortName: "Label",
namespace: "Inform",
dataProperties: {
labelid: { dataType: DT.Int64, isPartOfKey: true },
reportid: { dataType: DT.Int64 },
labelname: { dataType: DT.String },
ispublic: { dataType: DT.Bool },
mappedreports: { dataType: DT.Undefined }
},
navigationProperties: {
labels: {
entityTypeName: "Report:#Inform", isScalar: false,
associationName: "Report_Labels", foreignKeyNames: ["reportid"]
}
}
});
}
})
This is where I think the issue is I don't understand this adapter enough to ensure that I am receiving what I think I am as well as if it is handling the mapping correctly:
/* jsonResultsAdapter: parses report data into entities */
app.value('jsonResultsAdapter',
new breeze.JsonResultsAdapter({
name: "inform",
extractResults: function (data) {
var results = data.results;
if (!results) throw new Error("Unable to resolve 'results' property");
// Parse only the make and model types
return results && (results.reportHolder || results.labelHolder);
},
visitNode: function (node, parseContext, nodeContext) {
//Report parser
if (node.reportid && node.labels) {
node.mappedlabels = node.labels;
node.labels = [];
return { entityType: "Report" }
}
// Label parser
else if (node.labelid && node.reports) {
node.mappedreports = node.reports;
node.mappedreports = [];
return { entityType: "Label" };
}
}
}));
When I step through the code in chrome I can see that an object is returned. with 5 reports and each report has 5 labels ( I know the labels are showing null reports currently). When I set breakpoints within the jsonResultsAdapter I can see the result with 5 objects, but what gets passed back to the service is as a result is null. Can anyone help me verify if the model and mapping is correct or if you see anything out of place in the jsonResultsAdapter. I'd also appreciate any suggestions on things I may want to do different. I feel very black-box right now as I don't see/understand a good way to troubleshoot this mapping piece.
-cheers
Here I'll pick up on some of PW Kad's observations and add a few of my own.
Let's first understand the different roles of metadata and the JsonResultsAdapter.
Metadata is where you define the schema and validation rules for the client-side entity model. It describes the entity objects that Breeze keeps in cache and makes available to your program.
But the metadata have nothing to say about the JSON payload arriving from the server. That's a completely separate and lower level concern. That's the concern of the JsonResultsAdapter.
The JsonResultsAdapter sits in the pipeline between the JSON data arriving from the server as a result of an AJAX call ... and the entities in cache. The JSON data don't have to be shaped like the entities. They don't have to conform to the metadata you wrote. The metadata describe the entities as you would like to consume them. The JSON are the sad reality that the service gives you. The JsonResultsAdapter is there to bridge the gap.
Whether the entity schema conforms to the shape of the JSON payload is anyone's guess. Often the JSON data needs a little tweaking. It's the JsonResultsAdapter's job to manipulate the JSON "nodes" into something that Breeze can map into your entities. The job is easier if the JSON payload closely approximates the entity shape described by your metadata. Let's hope your JSON aligns well with your entities.
Metadata and materialization
Now Breeze does use the metadata when mapping the JSON into the entities. The MetadataStore has a NamingConvention that prescribes how to translate between the client entity property names and the service property names. The "materialization" process expects the JSON emerging from the JsonResultsAdapter to have the expected service property names. That's why I was adamant that the node property names (if you need them) be spelled in PascalCase ... assuming that you are using the standard Breeze camelCase convention and that your service does, in fact, spell property names in PascalCase.
Most C# and Java servers do. Rails and Node servers generally don't; they use camelCase on the server too ... which means you'd want NamingConvention.none if you're consuming feeds from these kinds of servers.
Ideally the JsonResultsAdapter has to do very little. The JSON property names typically map easily and obviously to the entity property names and you can handle whatever translation is needed with a NamingConvention. Such appears to be the case for you (see below).
For sure you're not accomplishing a thing with the code you showed us:
node.ReportId = node.ReportId;
node.ReportName = node.ReportName;
node.ReportDescription = node.ReportDescription;
That is the most elaborate "no op" code I've seen in a very long time. I wonder what you had in mind.
JsonResultsAdapter is often needed when identifying the EntityType corresponding to a JSON node. If you're not sourcing the data from .NET using the Json.Net serializer, your server may not be sending the type name down with the JSON data. Your JSON nodes are missing the $type property that Breeze is looking for by default.
If that's your situation (and it seems it is), your JsonResultsAdapter has to supply the type name.
Apparently, you can do that for your data by examining each node's key property. It seems that key property name contains within itself the distinguishing part of the type name.
Perhaps your JsonResultsAdapter.visitNode method could look like this:
visitNode: function (node, parseContext, nodeContext) {
//Report parser
if (node.ReportId) {
return { entityType: "Report:#Inform" }
}
// Label entity
else if (node.ReportLabels) {
return { entityType: "Label:#Inform" };
}
}
Notice that I included the namespace (:#Inform) in the entityType name property. The namespace is part of each EntityType's full name ... and you must supply it.
Notice that I did NOT do any property name mapping. I didn't see any reason for it. The node property names look just like the entity metadata names ... except for the PascalCasing ... and we take care of that with the NamingConvention.camelCase.
Bad Metadata?
Well actually the node property names do NOT look like the entity property names in your metadata, not even after accounting for the Pascal-to-camel-case conversion. I think this is what PW Kad was pointing out.
The problem is that the entity property names in your metadata are all lower case. Not camelCase; lower case. For example:
reportid: { dataType: DT.Int64, isPartOfKey: true },
reportname: { dataType: DT.String },
reportdescription: ...
Shouldn't they be:
reportId: { dataType: DT.Int64, isPartOfKey: true },
reportName: { dataType: DT.String },
reportDescription: ...
That would correspond nicely to your JSON property names
ReportId
ReportName
ReportDescription
Why would you want all lowercase property names on the client.
You could go all lowercase and write a really wacky custom NamingConvention to navigate between client entity names and service names. That's a lot of work to no purpose in my book.
Why is there no $type in your JSON?
I just scrolled to the top of this question and realized that your server is written in C# and it looks like you're using the Web API.
Why did you not annotate your Web API controller with the [BreezeController] attribute? Doing so would have configured your controller to serialize the data in the manner a Breeze client understands by default. You might not need a JsonResultsAdapter at all.
Don't change the type name!
Looking again I see yet another problem looming ahead. Your server-side class names have the suffix "Dto" but you don't want that suffix on your client type names. You are also changing the type name completely in one case: "ReportLabelDto" to "Label".
Breeze has a naming convention for morphing property names. It doesn't have a naming convention for "entity type" names.
It will be a royal pain if you insist on having different type names on the client and the server. I'm not sure it can be done.
Yes you can morph the entity name in the JsonResultsAdapter. That covers communications on the way in. But you also have to worry about the communications on the way out. The server is not going to be happy when you ask it to save an entity of class "Label" ... which it knows nothing about.
As I write I can't think of an easy way around this. At the moment, Breeze requires that the server-side type name be the same as the client EntityType name. If the type name on the server is "ReportLabelDto", you'll have to name the corresponding EntityType "ReportLabelDto". There is no easy way around that.
Fortunately, unlike property names which show up everywhere, you don't often refer to the EntityType name on the client so calling it "ReportLabelDto" shouldn't be a big deal.
It looks like all of your properties you are defining are not properly camelCased nor PascalCased. Breeze.js will look for properties that match - but unless I am missing something you have defined it will not toLowerCase them.
You need to set your model properties up like this -
ReportName: { dataType: DT.String },
and then in your results adapter you need to check for the property names properly like this -
if (node.ReportId && node.Labels) {
Thanks to PW Kad for pointing me in the right direction. I had forgotten the case sensitivity portion. In addition I went back and reassessed what I was trying to do in the jsonResultsAdapter.js file. I finally realized this file is working similar to automapper and I was under the impression that breeze would internally resolve the mappings. (maybe it does but with the EF context) but when creating client side meta I had to explicitly set the mappings. The updated code now shows:
app.value('jsonResultsAdapter',
new breeze.JsonResultsAdapter({
name: "inform",
extractResults: function (data) {
var results = data.results;
if (!results) throw new Error("Unable to resolve 'results' property");
// Parse only the make and model types
return results;
},
visitNode: function (node, parseContext, nodeContext) {
//Report parser
if (node) {
node.ReportId = node.ReportId;
node.ReportName = node.ReportName;
node.ReportDescription = node.ReportDescription;
node.ReportDateCreated = node.ReportDateCreated;
node.ReportOwner = node.ReportOwner;
node.ReportLabels = node.ReportLabels;
node.ReportLabels = [];
node.IsShared = node.IsShared;
node.IsFavorite = node.IsFavorite;
return { entityType: "Report" }
}
// Label parser
else if (node.ReportLabels) {
node.LabelId = node.LabelId;
node.LabelName = nodel.LabelName;
node.IsPrivate = node.IsPrivate;
node.IsFavorite = node.IsFavorite;
node.Reports = node.Reports;
node.Reports = [];
return { entityType: "Label" };
}
}
}));
I know I still have some tweaking to do on the mappings more than likely or how it is parsed from the API but making this change properly mapped the mock data and allowed it to bind/display within the UI.
Hope this helps