Refactoring rest request in SPFx Web aprt - rest

I have worked in a web part that fetch items from a SP list and then filter or groups these results.
For each filter action I send a new request to SP with Rest and I am using the rest code to get back the filtered items and show them in the web part.
Right now I have two filter actions triggered by onClick events.
Each action looks like this:
To filter by closed agreements:
private getEnded(): void {
this.props.provider.getEnded().then((originalitems: IList[]) => {
this.setState({
filteredListItems: originalitems,
filter: true
});
});
}
and to filter by last day passed:
private getPassed(): void {
this.props.provider.getPassed().then((originalitems: IList[]) => {
this.setState({
filteredListItems: originalitems,
filter: true
});
});
}
and in the dataprovider file I have these methods, each one of them make request to SharePoint, the only difference is the filter parameter:
To get closed agreements (called from the method getEnded()):
public async getEnded(): Promise<IList[]> {
let today = new Date();
let Agreements: IList[] = [];
const items = await sp.web.lists
.getByTitle("AgreementDatabase")
.items.select(this.select)
.filter(
`(AgreementEnded eq true) and (AgreementEndDate le '${today.toISOString()}')`
)
.expand("SalesManager,TaxCatchAll,FrameworkAgreement")
.get();
items.forEach(item => {
Agreements.push({
Title: item.Title,
Id: item.Id,
CustomerAgreementNr: item.CustomerAgreementNr,
AgreementType: item.AgreementType,
Customer: item.TaxCatchAll[0].Term,
FrameworkAgreement: item.FrameworkAgreement.Title,
ContactPerson: item.ContactPerson,
SalesManager: item.SalesManager.FirstName + " " + item.SalesManager.LastName,
DeliveryType: item.DeliveryType,
AgreementStartDate: item.AgreementStartDate,
AgreementEndDate: item.AgreementEndDate,
AgreementEnded: item.AgreementEnded,
LastPriceAdjustment: item.LastPriceAdjustment,
NextPriceAdjustment: item.NexPriceAdjustment,
});
});
return new Promise<IList[]>(async resolve => {
resolve(Agreements);
});
}
and to get passed passed agreements (called from the method getPassed()):
public async getPassed(): Promise<IList[]> {
let today = new Date();
let Agreements: IList[] = [];
const items = await sp.web.lists
.getByTitle("AgreementDatabase")
.items.select(this.select)
.filter(`LastPriceAdjustment le '${today.toISOString()}'`)
.expand("SalesManager,TaxCatchAll,FrameworkAgreement")
.get();
items.forEach(item => {
Agreements.push({
Title: item.Title,
Id: item.Id,
CustomerAgreementNr: item.CustomerAgreementNr,
AgreementType: item.AgreementType,
Customer: item.TaxCatchAll[0].Term,
FrameworkAgreement: item.FrameworkAgreement.Title,
ContactPerson: item.ContactPerson,
SalesManager: item.SalesManager.FirstName + " " + item.SalesManager.LastName,
DeliveryType: item.DeliveryType,
AgreementStartDate: item.AgreementStartDate,
AgreementEndDate: item.AgreementEndDate,
AgreementEnded: item.AgreementEnded,
LastPriceAdjustment: item.LastPriceAdjustment,
NextPriceAdjustment: item.NexPriceAdjustment,
});
});
return new Promise<IList[]>(async resolve => {
resolve(Agreements);
});
}
As you can see the method that request information from SP are almost identical, the only difference is the filter parameter.
I am wondering how can I resuse just one of this rest method tho fetch and filter data from sharepoint?
Best regards
Americo

You can write one method and pass the filer:
private getEnded(): void {
const filter = `(AgreementEnded eq true) and (AgreementEndDate le '${today.toISOString()}')`;
this.props.provider.getAgreements(filter).then((originalitems: IList[]) => {
this.setState({
filteredListItems: originalitems,
filter: true
});
});
private getPassed(): void {
const filter = `LastPriceAdjustment le '${today.toISOString()}'`;
this.props.provider.getAgreements(filter).then((originalitems: IList[]) => {
this.setState({
filteredListItems: originalitems,
filter: true
});
});
public async getAgreements(filterAgreements: string): Promise<IList[]> {
let today = new Date();
let Agreements: IList[] = [];
const items = await sp.web.lists
.getByTitle("AgreementDatabase")
.items.select(this.select)
.filter(filterAgreements)
.expand("SalesManager,TaxCatchAll,FrameworkAgreement")
.get();
items.forEach(item => {
Agreements.push({
Title: item.Title,
Id: item.Id,
CustomerAgreementNr: item.CustomerAgreementNr,
AgreementType: item.AgreementType,
Customer: item.TaxCatchAll[0].Term,
FrameworkAgreement: item.FrameworkAgreement.Title,
ContactPerson: item.ContactPerson,
SalesManager: item.SalesManager.FirstName + " " + item.SalesManager.LastName,
DeliveryType: item.DeliveryType,
AgreementStartDate: item.AgreementStartDate,
AgreementEndDate: item.AgreementEndDate,
AgreementEnded: item.AgreementEnded,
LastPriceAdjustment: item.LastPriceAdjustment,
NextPriceAdjustment: item.NexPriceAdjustment,
});
});
return new Promise<IList[]>(async resolve => {
resolve(Agreements);
});
Other improvement:
You can use .map() instead of .foreach()
Agreements = items.map(item => {
Title: item.Title,
Id: item.Id,
CustomerAgreementNr: item.CustomerAgreementNr,
AgreementType: item.AgreementType,
Customer: item.TaxCatchAll[0].Term,
FrameworkAgreement: item.FrameworkAgreement.Title,
ContactPerson: item.ContactPerson,
SalesManager: item.SalesManager.FirstName + " " + item.SalesManager.LastName,
DeliveryType: item.DeliveryType,
AgreementStartDate: item.AgreementStartDate,
AgreementEndDate: item.AgreementEndDate,
AgreementEnded: item.AgreementEnded,
LastPriceAdjustment: item.LastPriceAdjustment,
NextPriceAdjustment: item.NexPriceAdjustment,
});

Related

How to consolidate Cloudwatch lambda log groups in a common log group

My app has more than 100 lambdas on AWS as it works on a microservices pattern.
As configured by default, each lambda is writing on his own log group.
I'd like to get a view of the logs accross all my lambdas. I cannot use Logs Insights as I have more than 100 groups to search in.
Do you know how I can create a Log Group that would be an aggregation of all my lambda Log groups ?
Maybe there is a better way, but I ended up creating a Subscription Filter Lambda that copies all the logs to a single log group.
var aws = require("aws-sdk");
var zlib = require("zlib");
var { v4: uuid } = require("uuid");
const cloudwatch = new aws.CloudWatchLogs();
exports.handler = async function (input, context) {
const now = new Date();
const LOG_GROUP_NAME = "/aws/lambda/" + process.env.STAGE + "-main";
const LOG_STREAM_NAME =
now.getUTCFullYear() + "/" + now.getUTCMonth() + "/" + now.getUTCDay() + "/" + uuid();
try {
var zippedInput = new Buffer.from(input.awslogs.data, "base64");
const unzippedDataBuffer = await unzipLogs(zippedInput);
var awslogsData = JSON.parse(unzippedDataBuffer);
await cloudwatch
.createLogStream({
logGroupName: LOG_GROUP_NAME,
logStreamName: LOG_STREAM_NAME,
})
.promise();
await cloudwatch
.putLogEvents({
logGroupName: LOG_GROUP_NAME,
logStreamName: LOG_STREAM_NAME,
logEvents: awslogsData.logEvents.map((logEvent) => {
const { id, ...cleanEvent } = logEvent;
return cleanEvent;
}),
})
.promise();
} catch (err) {
console.error(error);
}
};
async function unzipLogs(zippedInput) {
return new Promise((resolve, reject) => {
zlib.gunzip(zippedInput, function (error, buffer) {
if (error) {
reject(error);
return;
}
resolve(buffer);
});
});
}

Uncaught (in promise) TypeError: Cannot use 'in' operator to search for 'validateStatus' in

I am getting ** Uncaught (in promise) TypeError: Cannot use 'in' operator to search for 'validateStatus' in 5f8425a33a14f026f80133ed** where 5f8425a33a14f026f80133ed is the id passed to the axios url
I want to display the services based on the user id. My url works perfectly in postman but when i access it from the veux store it gives an error.
services.js (store)
import axios from 'axios';
const state = {
services : {},
status: '',
error: null
};
const getters = {
services : state => { return state.services }
};
const actions = {
async fetchServices({commit}, userId) {
let res = await axios.get('http://localhost:5000/api/services/displayUser' , userId)
commit('setProducts', res.data)
return res;
}
};
const mutations = {
setProducts (state, items) {
state.services= items
},
};
export default {
state,
actions,
mutations,
getters
};
This is how I am calling the action :
computed: {
...mapGetters(["services"]),
},
methods: {
...mapActions(["fetchServices"]),
getData(){
this.fetchServices(this.user._id)
},
},
async created() {
await this.getProfile();
await this.getData();
}
The axios route is defined as
router.get('/displayUser', (req,res) => {
const query = user = req.body ;
Services.find(query)
.exec((err, services) => res.json(services))
})
the error screenshot :
Error screenshot
GET request should not have a body. Either use query params, indicate an id in a path, or use POST request.
In case of query params this may look like this:
let res = await axios.get('http://localhost:5000/api/services/displayUser' , { params: { userId })
router.get('/displayUser', (req,res) => {
const query = user = req.query;
Services.find(query)
.exec((err, services) => res.json(services))
})
This worked for me too:
In front end: Vue Js
let res = axios.get("http://localhost:3000/api/v1/role/getRoleByName",
{ params: { roleName: "name of role you want to send as params" },
});
In back end: Node Js
router.get('/getRoleByName', (req,res)=>{
let roleName = req.query.roleName;
roleModule.getRoleByName(roleName).then(data =>{
response.json(res,data)
}
).catch(err=> {
response.badRequest(res, err);
})
});
it's a silly mistake axios.post req.
async addTodo({ commit }, title) {
try {
const res = await axios.post(BASE_URL, { title, complete: false });
commit("newTodo", res.data);
} catch (err) {
console.log(err.message);
}
},

React Rest Calls Racing

I'm working on a web part using the SharePoint Framework, and my code is basically calling two methods to get information from SharePoint Online, when I call these methods on my render() method, I'm getting something like the following:
--- RENDER START ---
--- RENDER DONE ---
GetItems() => ID: 1 Title: New Office
GetItems() => ID: 2 Title: Mid-Term Reviews
GetItems() => ID: 3 Title: DevOps Training
GetItems() => ID: 4 Title: Work with us!
- GetAttachments() => ID: 2 FileName: reviews.jpeg
- GetAttachments() => ID: 1 FileName: new-office.jpg
- GetAttachments() => ID: 3 FileName: devops.jpeg
- GetAttachments() => ID: 4 FileName: careers.png
I was expecting to call these methods and have their results returned to me, and from there use them on my custom HTML rendering, the expected result would be something like this:
--- RENDER START ---
GetItems() => ID: 1 Title: New Office
- GetAttachments() => ID: 1 FileName: reviews.jpeg
GetItems() => ID: 2 Title: Mid-Term Reviews
- GetAttachments() => ID: 2 FileName: new-office.jpg
GetItems() => ID: 3 Title: DevOps Training
- GetAttachments() => ID: 3 FileName: devops.jpeg
GetItems() => ID: 4 Title: Work with us!
- GetAttachments() => ID: 4 FileName: careers.png
--- RENDER DONE ---
// uses the returned results in custom HTML for the rendering
Right now the code is running "wild", the htmlAttachments variable is receiving no results, and the rendering is completely blank, what should I do to get the results before the rendering?
Here it is my code:
public async GetItems() : Promise<IListItems>
{
let formattedResponse: IListItems = {value: []};
await pnp.sp.web.lists.getByTitle('LIST').items.select('Id, Title, Body, Link').get().then( response => {
response.map( (object: any, i: number) => {
formattedResponse.value.push( {Id : object.Id, Title: object.Title, Body: object.Body, Link : object.Link.Url} );
});
});
return formattedResponse;
}
public async GetAttachments(itemId: number) : Promise<IFileItems>
{
let formattedResponse: IFileItems = {value: []};
await pnp.sp.web.lists.getByTitle('LIST').items.getById(itemId).attachmentFiles.get().then( response => {
response.map( (object: any, i: number) => {
formattedResponse.value.push( {Id : itemId, FileName: object.ServerRelativeUrl} );
});
});
return formattedResponse;
}
public render() : void
{
let htmlAttachments: string = '';
htmlAttachments += '<ul>';
this.GetItems().then( (responseItems: IListItems) => {
responseItems.value.forEach( elementItems => {
console.log("GetItems() ==> ", elementItems.Id, elementItems.Title);
this.GetAttachments(elementItems.Id).then( (responseAtt: IFileItems) => {
responseAtt.value.forEach( elementAtt => {
console.log("\t GetAttachments() ==> ", elementAtt.Id, elementAtt.FileName);
htmlAttachments += '<li>' + 'ID: ' + elementAtt.Id + ' - Title: ' + elementAtt.FileName + '</li>';
});
});
});
});
htmlAttachments += '</ul>';
this.domElement.innerHTML = htmlAttachments;
}
A little late to this, but you can get rid of the GetAttachments method itself.
In the first call, GetItems itself you can get the attachment files and then use them in your HTML.
You can modify from below sample code:
public async GetItems() : Promise<IListItems>
{
let formattedResponse: IListItems = {value: []};
await pnp.sp.web.lists.getByTitle("LIST")
.items
.select("Id", "Title", "Body", "Link","AttachmentFiles")
.expand("AttachmentFiles")
.get()
.then(response => {
response.map((object: any, i: number) => {
formattedResponse.value.push({
Id : object.Id,
Title: object.Title,
Body: object.Body,
Link : object.Link.Url,
FileName: object.AttachmentFiles[0].FileName,
FileUrl : object.AttachmentFiles[0].ServerRelativeUrl
});
});
});
return formattedResponse;
}
Reference - Working with List Items in PnP JS

How to convert from get.JSON to fetch

I have this working fine with get.JSON but when I try and use the fetch API instead, it gives me the error "Required parameter: part".
export const fetchYoutube = () => {
return dispatch => {
fetchAsync()
.then(data => console.log(data))
.catch(reason => console.log(reason.message))
dispatch({
type: INCREMENT
})
}
}
async function fetchAsync () {
var query = {
part: 'snippet',
key: 'AIzaSyA3IHL73MF00WFjgxdwzg57nI1CwW4dybQ',
maxResults: 6,
type: 'video',
q: 'music'
}
let response = await fetch('https://www.googleapis.com/youtube/v3/search', {
data : query,
method: 'GET'
});
let data = await response.json();
return data;
}
How do I pass the query object using the fetch API?
Try attaching the query as params:
replace:
let response = await fetch('https://www.googleapis.com/youtube/v3/search', {
data : query,
method: 'GET'
});
with:
var url = new URL("https://www.googleapis.com/youtube/v3/search"),
query = {
part: 'snippet',
key: '#####################################',
maxResults: 6,
type: 'video',
q: 'music'
}
Object.keys(query).forEach(key => url.searchParams.append(key, query[key]))
let response = await fetch(url)
Setting query string using Fetch GET request

Cascading dropdown

Is there any alternative to json data to cascade dropdowns?
I am using country and state dropdown but cascading the data with json take too much time
[HttpGet]
public ActionResult States(int countryId)
{
DateTangoEntities _db = new DateTangoEntities();
var tset = _db.States.Where(r => r.CountryID == countryId).Select(r =>
new { r.StateName, r.StateID });
return Json(tset, JsonRequestBehavior.AllowGet);
}
and here is the jquery part
$(document).ready(function () {
var countries = $("#Country");
var regions = $("#States");
countries.change(function () {
regions.find('option').remove();
$.getJSON('/Profile/States', { countryId: countries.val() }, function (data) {
$(data).each(function () {
$('#States').append('<option value="' + this.StateID + '">' + this.StateName + '</option>').resetSS();
});
});
});
});