I'm trying to insert a many-to-many table with web api odata controls.
And I' ve created controls with scaffolding odata controllers with ef.
Everything is great. I can query the user table like this:
GET http://localhost:51875/odata/Users(1)?$expand=Roles
{
"odata.metadata": "http://localhost:51875/odata/$metadata#Users/#Element",
"Roles": [
{
"ID": 20,
"Name": "Admin"
}
],
"ID": 1,
"UserName": "user",
"Password": "pass",
"EnteredDate": "2017-12-07T14:55:22.24",
"LastLoginDate": null,
"Active": true
}
I've inserted the record 'Admin' manually. How can I add a new role for user?
I've tried,
PATCH http://localhost:51875/odata/Users(1)
{
"Roles": [
{
url : "http://localhost:51875/odata/Roles(10)"
}
],
}
it did not work. Can you help me?
Bit late perhaps but there is an answer to this, it is described on: learn.microsoft.com/...
Add the following CreateRef method to your UserController:
[AcceptVerbs("POST", "PUT")]
public IHttpActionResult CreateRef([FromODataUri] int key, string navigationProperty, [FromBody] Uri link)
{
var user = Db.Users
.FirstOrDefault(p => p.Id == key); //Get User by id
if (user == null)
{
return NotFound();
}
switch (navigationProperty)
{
case "Roles":
// You'll have to impement this 'getkey' method somehow.
// You could implement it yourself or take a look on the webpage I linked earlier.
var relatedKey = GetKeyFromUri(link);
var role = Db.Roles
.FirstOrDefault(f => f.Id == relatedKey); //Get Role by id
if (role == null)
{
return NotFound();
}
user.Roles.Add(role);
break;
default:
return StatusCode(HttpStatusCode.NotImplemented);
}
Db.SaveChanges();
return StatusCode(HttpStatusCode.NoContent);
}
Now you can add roles to a user with the following HTTP request:
PUT [...]/api/User(2)/Roles/$ref
Content-Type: application/json
Content-Length: 54
{ "#odata.id": "[...]/api/Role(4)/" }
Personally I don't find this method particularly nice but it is the standard. You could also do this with a custom 'AddRoles' action as you mention in your comment.
Related
I have a form where clients can register for contests. They submit their name and email address, and the registration gets stored in a table in the database which has a composite primary key of the contestId and the clientId.
If someone submits the form a second time (i.e., tries to register for the same contest a second time), Prisma throws a failed constraint error. That's fine, and even expected, but I'd like to return the existing entry, and am having a hard time constructing the query.
Schema:
model Client {
id Int #id #default(autoincrement())
email String #unique
first String?
last String?
registrations Registration[]
}
model Contest {
id Int #id #default(autoincrement())
name String #unique
registrations Registration[]
}
model Registration {
contest Contest #relation(fields: [contestId], references: [id])
contestId Int
contestant Client #relation(fields: [clientId], references: [id])
clientId Int
points Int
##id ([contestId, clientId])
}
Registration:
try {
const registration = await prisma.registration.create({
data: {
points: 1,
contest: {
connectOrCreate: {
where: {
name: contest,
},
create: {
name: contest,
}
}
},
contestant: {
connectOrCreate: {
where: {
email: email,
},
create: {
first: first,
last: last,
email: email,
},
},
},
}
});
return res.status(201).send({ ...registration});
}
For new registrants that works, but if the registration already exists, I end up in the catch block. I assume this is the right way to do this — as opposed to, say, querying for existence first, because that seems expensive given that it's likely to be very rare that someone accidentally tries to re-register — but if this isn't a best practice on how to handle things, I'm open to other suggestions.
In the catch block, I then need to find the existing entry, but neither of the two things I've tried have worked:
catch (error) {
// entry already exists
if ('P2002' === error.code) {
const registration = await prisma.registration.findUnique({
where: {
contest: {
is: {
name: contest,
},
},
contestant: {
is: {
email: email,
},
},
},
})
return res.status(200).send(...registration);
}
return res.status(500).end(`Register for contest errorr: ${error.message}`);
}
complains Argument where of type RegistrationWhereUniqueInput needs exactly one argument, but you provided contest and contestant.
And
const registration = await prisma.registration.findUnique({
where: {
contestId_clientId: {
contest: {
is: {
name: contest,
},
},
contestant: {
is: {
email: email,
},
},
},
},
})
complains
Unknown arg `contest` in where.contestId_clientId.contest for type RegistrationContestIdClientIdCompoundUniqueInput. Did you mean `contestId`?
Unknown arg `contestant` in where.contestId_clientId.contestant for type RegistrationContestIdClientIdCompoundUniqueInput. Did you mean `contestId`?
Argument contestId for where.contestId_clientId.contestId is missing.
Argument clientId for where.contestId_clientId.clientId is missing.
I feel like this latter approach of using Prisma's auto-generated contestId_clientId is directionally right, but how do I construct the Prisma query to find it, starting from having the contest name and client email?
Instead of finding a unique record from the registration model, you can do a query something like this to find the Contest and Client details. The only valid argument for findUnique in the registration model would be: contestId_clientId and you can only pass contestId and clientId in them, other values are not allowed.
const registration = await prisma.client.findUnique({
where: {
email: 'email',
},
include: {
registrations: {
select: {
contest: contest,
points: true,
},
},
},
});
Hello I have been having hard time solving the following problem I would like to show only order notes from woocommmerce with the value "customer_note": true , see the Json request downward
{
"id": 281,
"author": "system",
"date_created": "2017-03-21T16:46:41",
"date_created_gmt": "2017-03-21T19:46:41",
"note": "Order ok!!!",
"customer_note": false,
}
This is how the data from order notes is held in my flutter code.
#override
Future<List<OrderNote>> getOrderNote(
{String? userId, String? orderId}) async {
try {
var response = await wcApi
.getAsync('orders/$orderId/notes?customer=$userId&per_page=20');
var list = <OrderNote>[];
if (response is Map && isNotBlank(response['message'])) {
throw Exception(response['message']);
} else {
for (var item in response) {
// if (item.type == 'customer') {
/// it is possible to update to `any` note
/// ref: https://woocommerce.github.io/woocommerce-rest-api-docs/#list-all-order-notes
list.add(OrderNote.fromJson(item));
// }
}
return list;
}
} catch (e) {
rethrow;
}
}
What about adding a condition inside the for loop to check the value of customer_note in the item to decide whether to add it to the list or not like the following:
for (var item in response) {
if (item['customer_note'])
{
// customer_note == true
list.add(OrderNote.fromJson(item));
}
}
I Also filter the order note containing a specific word with the following
if (item['note'].contains('État'))
{
list.add(OrderNote.fromJson(item));
}
I'm trying to build a postman GET request in order to retrieve an entry that I have in a MongoDB by using the unique id generated in the database.
To be more precise, I am interested in writing a GET request to retrieve for example the next entry :
{
"id": "61a51cacdfb9ea1bd9395874",
"Name": "asdsd",
"Code": "asdca",
"Weight": 23,
"Price": 23,
"Color": "sfd",
"isDeleted": false
}
Does anyone have an idea how to include that id in the GET request in order to retrieve the prodcut from above?
Thanks!
EDIT :
#J.F thank you for the kind response and information provided, but unforunately, it still does not work :(.
Those are the products that I have right now, and I tried to GET the one with id = 61a51cacdfb9ea1bd9395874
And this is the response I got :
Also, this is the logic that I implemented for the GET request :
filename : product.service.ts
async getSingleProduct(productId: string) {
const product = await this.findProduct(productId);
return {
id: product.id,
Name: product.Name,
Code: product.Code,
Weight: product.Weight,
Price: product.Price,
Color: product.Price,
isDeleted: product.isDeleted };
}
private async findProduct(id: string): Promise<Product> {
let product;
try {
const product = await this.productModel.findById(id)
} catch (error) {
throw new NotFoundException('Product not found');
}
if (!product) {
throw new NotFoundException('Product not found');
}
return product;
}
filename : product.controller.ts
#Get(':id')
getProduct(#Param('id') prodId: string) {
return this.productsService.getSingleProduct(prodId)
}
EDIT2 :
#Controller('produse')
export class ProductsController {
constructor(private readonly productsService: ProductsService) {}
#Post()
async addProduct(
#Body('Name') prodName: string,
#Body('Code') prodCode: string,
#Body('Weight') prodWeight: number,
#Body('Price') prodPrice: number,
#Body('Color') prodColor: string,
#Body('isDeleted') prodIsDeleted: boolean,
) {
const generatedId = await this.productsService.createProduct(
prodName,
prodCode,
prodWeight,
prodPrice,
prodColor,
prodIsDeleted
);
return { id: generatedId };
To implement a RESTful API your endpoints has to be like this:
Verb
Path
GET
/resource
GET
/resource/:id
POST
/resource
PUT
/resource/:id
DELETE
/resource/:id
The path you want is GET /resource/:id
The id is used into route because is unique and identify a resource.
So your path can be something like:
http://localhost:8080/v1/resource/61a51cacdfb9ea1bd9395874
I have two collection type Organizations and Employees.
Its a one to many relationship between Organization and Employee.
I want to add new employee in organization through rest api call in strapi.
Strapi doesn't provide :
http://localhost:1337/organizations/{id}/employees
How to do the update?
There is no already built in solution to do it.
But in Strapi you can create your own controller to match that route and handle all the logic needed in there.
You can check the info about custom controllers here
As Christian said, you will have to create your own route in your Organization API.
Create your route.
Path — api/organization/config/routes.json
{
"routes": [
{
"method": "POST",
"path": "/organizations/:id/employees",
"handler": "organization.cusom",
"config": {
"policies": []
}
},
...
]
}
Create the controller function.
To do so, I will copy the function from the default create one:
https://strapi.io/documentation/3.0.0-beta.x/concepts/controllers.html#core-controllers
Path — `api/organization/controllers/organizaion.js``
const { parseMultipartData, sanitizeEntity } = require('strapi-utils');
module.exports = {
async custom(ctx) {
let entity;
if (ctx.is('multipart')) {
const { data, files } = parseMultipartData(ctx);
entity = await strapi.services.employee.create(data, { files });
} else {
entity = await strapi.services.employee.create(ctx.request.body);
}
return sanitizeEntity(entity, { model: strapi.models.employee });
},
};
And set employee for the creation cause we want to create employee.
Force to use the right organization ID
module.exports = {
async custom(ctx) {
// get the id params from the URL
const {id} = ctx.params;
// force the relation to this specific organisation
ctx.request.body.organization = id;
if (ctx.is('multipart')) {
const { data, files } = parseMultipartData(ctx);
entity = await strapi.services.employee.create(data, { files });
} else {
entity = await strapi.services.employee.create(ctx.request.body);
}
return sanitizeEntity(entity, { model: strapi.models.employee });
},
};
Is there a recommended practice for adding non-persistent attributes to an Objection model object such that it that won't overwrite predefined attributes?
Objection models have the virtualAttributes field. From the documentation:
The virtual values are not written to database. Only the “external” JSON format will contain them.
It is important to note these are functions, not just model properties.
Example from the docs:
class Person extends Model {
static get virtualAttributes() {
return ['fullName', 'isFemale'];
}
fullName() {
return `${this.firstName} ${this.lastName}`;
}
get isFemale() {
return this.gender === 'female';
}
}
const person = Person.fromJson({
firstName: 'Jennifer',
lastName: 'Aniston',
gender: 'female'
});
console.log(person.toJSON());
// --> {"firstName": "Jennifer", "lastName": "Aniston", "isFemale": true, "fullName": "Jennifer Aniston"}