I have this single type in my strapi dashboard :
I Have a component called Logo
Another component called Links, it contains another component called Link
Finally a component called MenuButton.
When I go to http://localhost:1337/api/global?populate=* I got :
{
"data": {
"id": 1,
"attributes": {
"createdAt": "2021-12-27T11:54:36.177Z",
"updatedAt": "2021-12-27T11:54:54.737Z",
"publishedAt": "2021-12-27T11:54:54.731Z",
"logo": {
"id": 1,
"name": null
},
"navigation": {
"id": 1 // why I don't get links here ?
},
"menuButton": {
"id": 1,
"icon": ""
}
}
},
"meta": {
}
}
I Already published my content and allowed permissions for public.
My question is :
How can I access to the links inside navigation object ?
See my earlier answer here
Strapi 4 requires you to populate your request (see: population documentation )
which could look like this (for level 2 population):
// populate request
const qs = require('qs')
const query = qs.stringify(
{
populate: {
Product: {
populate: ['Image']
}
}
},
{
encodeValuesOnly: true
}
)
// get id
const id = yourId
// get rquest
const Response= await axios.get(
`http://localhost:1337/api/[your api]/${id }/?${query}`
)
Now media links should be included in your response
To retrieve up to 5 levels deep, you can install this package npm i strapi-plugin-populate-deep
I am trying to display a list of unique subject categories on a Gatsby site, which I will use to create unique pages. These will serve as taxonomy terms, of sorts. A limited version of my source json file looks like:
[
{
"BookID": "4176",
"Title": "Book Title 1",
"Subject": {
"subjectID": "HR",
"name": "Civil War & Reconstruction"
}
},
{
"BookID": "3619",
"Title": "Book Title 2",
"Subject": {
"subjectID": "AR",
"name": "Fine Art & Photography"
}
},
{
"BookID": "3619",
"Title": "Book Title 3",
"Subject": {
"subjectID": "AR",
"name": "Fine Art & Photography"
}
}
]
In my gatsby-node.js file, I can create pages using a list of distinct values of IDs to serve as the slugs to create my subject categories. As below:
allSubjects: allBooksJson {
distinct(field: Subject___subjectID)
}
However, I also need the name associated with these. I have not yet seen a way to use this as a filter, in order to deduplicate the results of a query.
So what I would ultimately like to is return all the unique subject objects so I can use the subjectID as a slug and the full name where needed on the individual pages.
Still learning Gatsby, so this may be the wrong approach, and any advice would be appreciated.
The idea of creating dynamic pages, is to get all the needed values in your gatsby-node.js using a GraphQL query, to create a bunch of pages and then, use the context to send a unique identifier to the template, to filter again the pages to get the specific data for each entry (books in your case). So:
const path = require(`path`)
const { createFilePath } = require(`gatsby-source-filesystem`)
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const result = await graphql(`
query {
allBookJson {
edges {
node {
Subject{
subjectID
}
}
}
}
}
`)
result.data.allBookJson.edges.forEach(({ node }) => {
createPage({
path: `books/${node.Subject.subjectID}`, // change it as you wish
component: path.resolve(`./src/templates/book.js`), // change it as you wish
context: {
subjectID: node.fields.slug,
},
})
})
}
Note: adapt the snippet (query, loop, and variables) to your needs. You don't need to filter anything at this point, since you are only fetching the subjectID of all books.
If the values are likely to be repeated, use the new Set to remove the duplicates, then, you can loop through them to create pages dynamically:
let unique = [...new Set(result.data.allBookJson.edges.node)];
You are sending the subjectID to your templates/book.js file via context, so it will be available to be used as a pageContext.
Anytime you want just to get a list of all books, you can create a page query or a static query and loop through them at any time.
import React from "react"
import { graphql } from "gatsby"
import Layout from "../components/layout"
export default function Book({ data }) {
const books = data.allBookJson
return (
<Layout>
<div>
{books.map(book=>{
return <div>book.title</div>
})}
</div>
</Layout>
)
}
export const query = graphql`
query($subjectID: String) {
allBookJson(Subject___subjectID: { eq: $subjectID } ) {
edges{
node{
title
}
}
}
}
`
Note: again, test your query and adapt it to your needs at localhost:8000/___graphql. If you have duplicate results use the new Set.
It's difficult to guess your data structure without knowing it properly, the idea is to create a unique query based on the context value subjectID and filter the values. Use the GraphQL playground as support to know how the query and the filters should look like.
Further details: https://www.gatsbyjs.com/docs/tutorial/part-seven/
I am exploring if I can use cypress for end-to-end testing or not in angular? I am super beginner in it.
Cypress has some server() instance for XHR.
Question:
Suppose I am testing the login page so I can write test cases for querying elements and do the validation. In this process the browser will be making some API call, will it possible to write test cases for validating what was the statusCode the API had retured? What was XHR API response etc?
of course. With cypress you can spy the requests or mock them.
I have written a quick example to show you both methods:
describe("test", () => {
it("spy", () => {
cy.server();
cy.route("POST", /.*queries.*/).as("request")
cy.visit("https://docs.cypress.io/")
.get("#search-input").type("1234567890")
.wait("#request").then(xhr => {
expect(xhr.status).to.eq(200)
})
})
it("mock", () => {
cy.server();
const obj = JSON.parse(`
{
"results": [{
"hits": [{
"hierarchy": {
"lvl2": null,
"lvl3": null,
"lvl0": "Podcasts",
"lvl1": null,
"lvl6": null,
"lvl4": null,
"lvl5": null
},
"url": "https://stackoverflow.com",
"content": "mocked",
"anchor": "sidebar",
"objectID": "238538711",
"_snippetResult": {
"content": {
"value": "mocked",
"matchLevel": "full"
}
},
"_highlightResult": {
"hierarchy": {
"lvl0": {
"value": "Podcasts",
"matchLevel": "none",
"matchedWords": []
}
},
"content": {
"value": "mocked",
"matchLevel": "full",
"fullyHighlighted": false,
"matchedWords": ["testt"]
}
}
}
]
}
]
}
`);
cy.route("POST", /.*queries.*/, obj)
cy.visit("https://docs.cypress.io/")
.get("#search-input").type("1234567890")
.get("#algolia-autocomplete-listbox-0").should("contain", "mocked")
})
})
The spy example receives the raw XHR object and thus you are able to check the status code and so on.
The mock example shows you how you can mock any ajax request.
Please note: Currently you can not spy & mock fetch requests. But as far as I know they are rewriting the network layer in order to make this possible. Let me know if you need further assistance
My company has made a custom photo-field in Sharepoint for it's news. I'm trying to use Microsoft Graph to fetch the images, but with no success.
This is the columns description:
{
"columnGroup": "Page Layout Columns",
"description": "",
"displayName": "Thumbnail image",
"enforceUniqueValues": false,
"hidden": false,
"id": "XXXXXXX-XXXX-XXXX-XXX-XXXXXXXXXXXX",
"indexed": false,
"name": "PublishingPageImage",
"readOnly": false,
"required": false
},
In the documentation for Microsoft Graph it is written that you can make a request like this
GET https://graph.microsoft.com/beta/sites/{site-id}/lists/{list-id}/items?expand=fields(select=Column1,Column2)
Although - no matter how I seem to write the request, i can't get the image field.
My most recent try has been this request:
https://graph.microsoft.com/beta/sites/knowit.sharepoint.com/lists/posts/items?expand=fields(select=PublishingPageImage)
The respons I got from Microsoft was this:
{
"error": {
"code": "-1, Microsoft.SharePoint.Client.ClientServiceException",
"message": "Cannot serialize data for type Microsoft.SharePoint.Publishing.Fields.ImageFieldValue.",
"innerError": {
"request-id": "f25e4851-0c1b-4061-ad6a-948d38004046",
"date": "2018-09-17T14:03:01"
}
}
}
Should I use something like .value or .data or .ImageUrl after the request? If i get a link or a data-value doesn't really matter. In the call for /me/ for Microsoft users there is a $value property for getting the user profile photo. Is it something like this?
It depends how image is stored.
If it is attachment you can do following:
string baseURL = $"https://{spTenant}/_api/web/lists('{ AA.config.listID}')/items({ siteData.Id})/AttachmentFiles";
string fileName = await GetFileNameAsync(baseURL);
string getPictureReqUrl = $"{baseURL}('{fileName}')/$value";
Stream responseStream = await GetPictureAsync(getPictureReqUrl);
private static async Task<Stream> GetPictureAsync(string reqUrl)
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", SPOToken);
HttpResponseMessage response = await client.GetAsync(reqUrl);
return await response.Content.ReadAsStreamAsync();
}
Important! This is not supported by graph yet, but you can use SharePoint Rest API
If picture is stored in document library you need to use Drive object instead
https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/resources/drive
Pretty straightforward (I hope). I'd like to be able to use the API endpoint and have it only return specified fields. I.E. something like this
http://localhost:1337/api/reference?select=["name"]
Would ideally return something of the form
[{"name": "Ref1"}]
Unfortunately that is not the case, and in actuality it returns the following.
[
{
"contributors": [
{
"username": "aduensing",
"email": "standin#gmail.com",
"lang": "en_US",
"template": "default",
"id_ref": "1",
"provider": "local",
"id": 1,
"createdAt": "2016-07-28T19:39:09.349Z",
"updatedAt": "2016-07-28T19:39:09.360Z"
}
],
"createdBy": {
"username": "aduensing",
"email": "standin#gmail.com",
"lang": "en_US",
"template": "default",
"id_ref": "1",
"provider": "local",
"id": 1,
"createdAt": "2016-07-28T19:39:09.349Z",
"updatedAt": "2016-07-28T19:39:09.360Z"
},
"updatedBy": {
"username": "aduensing",
"email": "standin#gmail.com",
"lang": "en_US",
"template": "default",
"id_ref": "1",
"provider": "local",
"id": 1,
"createdAt": "2016-07-28T19:39:09.349Z",
"updatedAt": "2016-07-28T19:39:09.360Z"
},
"question": {
"createdBy": 1,
"createdAt": "2016-07-28T19:41:33.152Z",
"template": "default",
"lang": "en_US",
"name": "My Question",
"content": "Cool stuff, huh?",
"updatedBy": 1,
"updatedAt": "2016-07-28T19:45:02.893Z",
"id": "579a5ff83af4445c179bd8a9"
},
"createdAt": "2016-07-28T19:44:31.516Z",
"template": "default",
"lang": "en_US",
"name": "Ref1",
"link": "Google",
"priority": 1,
"updatedAt": "2016-07-28T19:45:02.952Z",
"id": "579a60ab5c8592c01f946cb5"
}
]
This immediately becomes problematic in any real world context if I decide to load 10, 20, 30, or more records at once, I and end up loading 50 times the data I needed. More bandwidth is used up, slower load times, etc.
How I solved this:
Create custom controller action (for example, 'findPaths')
in contributor/controllers/contributor.js
module.exports = {
findPaths: async ctx => {
const result = await strapi
.query('contributor')
.model.fetchAll({ columns: ['slug'] }) // here we wait for one column only
ctx.send(result);
}
}
Add custom route (for example 'paths')
in contributor/config/routes.json
{
"method": "GET",
"path": "/contributors/paths",
"handler": "contributor.findPaths",
"config": {
"policies": []
}
},
Add permission in admin panel for Contributor entity, path action
That's it. Now it shows only slug field from all contributor's records.
http://your-host:1337/contributors/paths
Here is how you can return specific fields and also exclude the relations to optimize the response.
async list (ctx) {
const result = await strapi.query('article').model.query(qb => {
qb.select('id', 'title', 'link', 'content');
}).fetchAll({
withRelated: []
}).catch(e => {
console.error(e)
});
if(result) {
ctx.send(result);
} else {
ctx.send({"statusCode": 404, "error": "Not Found", "message": "Not Found"});
}
}
I know this is old thread but I just run into exactly same problem and I could not find any solution. Nothing in the docs or anywhere else.
After a few minutes of console logging and playing with service I was able to filter my fields using following piece of code:
const q = Post
.find()
.sort(filters.sort)
.skip(filters.start)
.limit(filters.limit)
.populate(populate);
return filterFields(q, ['title', 'content']);
where filterFields is following function:
function filterFields(q, fields) {
q._fields = fields;
return q;
}
It is kinda dirty solution and I haven't figured out how to apply this to included relation entites yet but I hope it could help somebody looking for solution of this problem.
I'm not sure why strapi does not support this since it is clearly capable of filtering the fields when they are explicitly set. it would be nice to use it like this:
return Post
.find()
.fields(['title', 'content'])
.sort(filters.sort)
.skip(filters.start)
.limit(filters.limit)
.populate(populate);
It would be better to have the query select the fields rather than relying on node to remove content. However, I have found this to be useful in some situations and thought I would share. The strapi sanitizeEntity function can include extra options, one of which allows you only include fields you need. Similar to what manually deleting the fields but a more reusable function to do so.
const { sanitizeEntity } = require('strapi-utils');
let entities = await strapi.query('posts').find({ parent: parent.id })
return entities.map(entity => {
return sanitizeEntity(entity, {
model: strapi.models['posts'],
includeFields: ['id', 'name', 'title', 'type', 'parent', 'userType']
});
});
This feature is not implemented in Strapi yet. To compensate, the best option for you is probably to use GraphQL (http://strapi.io/documentation/graphql).
Feel free to create an issue or to submit a pull request: https://github.com/wistityhq/strapi
You can use the select function if you are using MongoDB Database:
await strapi.query('game-category').model.find().select(["Code"])
As you can see, I have a model called game-category and I just need the "Code" field so I used the Select function.
In the current strapi version (3.x, not sure about previous ones) this can be achieved using the select method in custom queries, regardless of which ORM is being used.
SQL example:
const restaurant = await strapi
.query('restaurant')
.model.query((qb) => {
qb.where('id', 1);
qb.select('name');
})
.fetch();
not very beautiful,but you can delete it before return.
ref here:
https://strapi.io/documentation/developer-docs/latest/guides/custom-data-response.html#apply-our-changes
const { sanitizeEntity } = require('strapi-utils');
module.exports = {
async find(ctx) {
let entities;
if (ctx.query._q) {
entities = await strapi.services.restaurant.search(ctx.query);
} else {
entities = await strapi.services.restaurant.find(ctx.query);
}
return entities.map(entity => {
const restaurant = sanitizeEntity(entity, {
model: strapi.models.restaurant,
});
if (restaurant.chef && restaurant.chef.email) {
**delete restaurant.chef.email;**
}
return restaurant;
});
},
};
yeah,I remember another way.
you can use the attribute in xx.settings.json file.
ref:
model-options
{
"options": {
"timestamps": true,
"privateAttributes": ["id", "created_at"], <-this is fields you dont want to return
"populateCreatorFields": true <- this is the system fields,set false to not return
}
}
You can override the default strapi entity response of:-
entity = await strapi.services.weeklyplans.create(add_plan);
return sanitizeEntity(entity, { model: strapi.models.weeklyplans });
By using:-
ctx.response.body = {
status: "your API status",
message: "Your own message"
}
Using ctx object, we can choose the fields we wanted to display as object.
And no need to return anything. Place the ctx.response.body where the response has to be sent when the condition fulfilled.
It is now 2023, and for a little while it has been possible to do this using the fields parameter:
http://localhost:1337/api/reference?fields[0]=name&fields[1]=something