Add virtual properties in Strapi - strapi

Is there a way to add virtual property in api response object ?
I tried to do this in the controller, but the values I add are not displayed in the API return.
My goal is to dynamically define the value of the virtual field according to the current day.
findOne: async ctx => {
if (!ctx.params._id.match(/^[0-9a-fA-F]{24}$/)) {
return ctx.notFound();
}
const cake = strapi.services.cake.fetch(ctx.params);
cake.virtualproperty = "test to add value in api return";
return cake;
}
ps: I try to do this in strapi cake example project

You missed to await your function.
Should be this following code:
findOne: async ctx => {
if (!ctx.params._id.match(/^[0-9a-fA-F]{24}$/)) {
return ctx.notFound();
}
const cake = await strapi.services.cake.fetch(ctx.params);
cake.virtualproperty = "test to add value in api return";
return cake;
}

Related

Sinon with complex object structure

I'm using #types/xrm and attempting to test a method call with sinon. Unfortunately I am hitting quite a few issues due to the complex nature of the return and call I need to mock. I can find really simple examples of sinon stubbing or spying on calls, but nothing more complex than that.
I have the following simple code:
export class AccountForm {
static Fields = class {
static PrimaryContact = "primarycontactid";
static Name = "name";
}
public onLoad(context: Xrm.Events.EventContext): void {
// Get the form context
const formContext = context.getFormContext();
// Get the attributes required
const primaryContact = formContext.getAttribute(AccountForm.Fields.PrimaryContact);
const name = formContext.getAttribute(AccountForm.Fields.Name);
// Add our onchange events
primaryContact.addOnChange(this.onChangePrimaryContact);
name.addOnChange(this.onChangeName);
}
public async onChangePrimaryContact(context: Xrm.Events.EventContext): Promise<void> {
alert("Do something");
}
public async onChangeName(context: Xrm.Events.EventContext): Promise<void> {
alert("Do something else");
}
}
I want to test that an onchange event has been registered to both fields. Ideally, I'd like to check it's the RIGHT onchange, but I'll settle with the fact that it's been called once.
The "easy" way has been to check that the addOnChange method was called twice, this is as below:
import {AttributeMock, XrmMockGenerator} from "xrm-mock";
import * as sinon from "sinon";
import { AccountForm } from "../../src/entities/Account/Form";
describe("Account Form Tests", () => {
describe("Check Onload", () => {
beforeEach(() => {
XrmMockGenerator.initialise();
XrmMockGenerator.Attribute.createString("name", "");
XrmMockGenerator.Attribute.createLookup("primarycontactid", []);
});
it("should register onChange functions", () => {
// Arrange
let formContext = XrmMockGenerator.getFormContext();
let context = XrmMockGenerator.getEventContext();
// Stub
const attributeStub = sinon.stub(AttributeMock.prototype, "addOnChange");
// Act
let form = new AccountForm();
form.onLoad(context);
// Assert
expect(attributeStub.calledTwice).toBeTruthy();
});
});
});
But this is not very resilient, as it is not checking WHICH attributes the onChange functions were added to, or what function was registered.
I've tried stubbing the ForContext's "GetAttribute", but looks like it's requiring me to mock the entire return object, as otherwise, the stub does not return anything? I can get around this with using spy, but still can't work out how to check the attribute that the onChange is being added to and what the function is
Am I missing something obvious here?

Cannot call method - mocha

I am running some test in my first smart contract called Inbox.sol.
I want to call the method .message in order to see if it is equal to the default variable I use when I deploy the contract with web3.
My Inbox.sol code
pragma solidity >=0.6.12;
contract Inbox {
string public message;
function initialInbox(string memory initialMessage) public {
message = initialMessage;
}
function setMessage(string memory newMessage) public {
message = newMessage;
}
}
My test file with Mocha is:
let accounts
let inbox
beforeEach(async () => {
// Get a list of all accounts
accounts = await web3.eth.getAccounts()
// Use one of those account to deploy the contract
inbox = await new web3.eth.Contract(abi)
.deploy({ data: bytecode, arguments: [INITIAL_ARGUMENT]})
.send({ from: accounts[0], gas: GAS})
})
describe(('Inbox'), () => {
it("has a default message", async () => {
const message = await inbox.methods.message() // Returns a big object
console.log(message)
const messageCalled = await inbox.methods.message().call()
console.log(messageCalled) // Returns nothing
assert.strictEqual(message, INITIAL_ARGUMENT)
})
})
Your JS inbox variable is an instance of web3.eth.Contract (docs).
The inbox.methods.message() is an instance of a helper object (that's the "Returns a big object" in your comment) containing the .call() method, that you need to use when you want to perform a read-only call.
const message = await inbox.methods.message().call() // Returns the string
Docs: https://web3js.readthedocs.io/en/v1.3.4/web3-eth-contract.html#methods-mymethod-call
Change your code and add a contractor.
function constructor(string memory initialMessage) public {
message = initialMessage;
}
if you are using an older version of solidity for example ^0.4.17. you can create a constructor by create a method same name as your contract class.
function Inbox(string memory initialMessage) public {
message = initialMessage;
}

How to subscribe new value in Akavache?

I'm using Akavache's GetAndFetchLatest method and I have created dependency services to communicate with Akavache's method. I'm calling akavache from service layer successfully when i directly reference. For subscribing
MyMod result = null;
var cache = BlobCache.LocalMachine;
var cachedPostsPromise = cache.GetAndFetchLatest(
"mykey",
() => GetInfo(),
offset =>
{
//some condition
});
cachedPostsPromise.Subscribe(subscribedPosts => {
Device.BeginInvokeOnMainThread(() =>
{
//do sothing.
});
});
result = await cachedPostsPromise.FirstOrDefaultAsync();
return result;
It works.But how an I call subscribe on service layer with interface/dependency service?
I think you are new to reactive programming. Understanding the basic principles helps when using Akavache. Maybe this intro helps.
To answer your question, place code like this in your "repository" class:
public override IObservable<MyClass> Get(string key)
{
var cachedObservable = blobCache.GetAndFetchLatest<MyClass>(key,
() => GetFromServerAsync(key));
return cachedObservable ;
}
And in the caller:
private void getNewData()
{
var myClassObservable = myRepository.Get("the key");
myClassObservable.Subscribe(handleNewMyClass);
}
private void handleNewMyClass(MyClass newClass)
{
//handle the new class
}
Note that handleNewMyClass() is called twice:
first with the MyClass from cache
then with the MyClass that was fetched (from the server)
Using this approach you can simply place the repository class in your IoC Container.
Please find the the sample code :
var result = BlobCache.LocalMachine;
var cachedPostsPromise = cache.GetAndFetchLatest(
"mykey",
() => ViewModelLocator.GetInstance<IYourServiceName>().MethodName(),
offset =>
{
//some condition
});
cachedPostsPromise.Subscribe(subscribedPosts => {
Device.BeginInvokeOnMainThread(() =>
{
//Your piece of code
});
});
result = await cachedPostsPromise.FirstOrDefaultAsync();
return result;
Please note the there any anything inside subscribed will be called twice : first set of data will be cache and second set will be freshly fetched from server.You have to manage according.

Angular 5 download docx file from c# web api

c# Model:
public class docxModel
{
public byte[] testDocx { get; set; }
}
c# web api file return method code:
byte[] tDocx = File.Exists(wordFilePath) ? File.ReadAllBytes(wordFilePath):null;
docxModel.testDocx = tDocx;
return docxModel;
Angular model:
export interface ITestDocxModel {
testDocx: Blob;
}
Angular service has a method called CreateWordDoc which will call web api and maps above mentioned c# model to angular model.
Angular component:
async printToWord() {
await this.testService.CreateWordDoc()
.then((res: ITestDocxModel) => {
this.testModel = res;
this.testDocx = this.testModel.testDocx;
var testBlob = new Blob([this.testDocx], { type: 'text/docx' });
var url = window.URL.createObjectURL(testBlob);
window.open(url);
})
.catch(err => {
this.handleError(err);
});
}
When I call print to word method from button click then it opens up a new window and shows binary data.
I would like it to open word document with save as option.
Any help is appreciated.
Thank you!

How can I create a typed collection class in Knockout mapping?

I'm using knockout with the mapping plugin so that I can write code like this:
function ContactViewModel() {
//other properties...
this.emails = ko.observableArray();
}
function ContactEmailViewModel() {
//other properties...
this.address = ko.observable();
}
var koContactMap = {
'emails': {
create: function (options) {
return new ContactEmailViewModel(options.data);
}
}
};
var model = new ContactViewModel();
ko.mapping.fromJS([JSON data from web service], koContactMap, model);
In English, I have contacts and emails, and a contact has emails.
This works perfectly, and I can load the contact and have the data and emails populated, and the emails are of type ContactEmailViewModel, as I want.
But the thing I'm confused about is: why does the map create method return a singular email object instead of a collection of email objects. The emails property is a collection, but seems to be populated by returning a single object, and is called multiple times, once for each member.
This correctly populates the emails property. But now I want to change emails from an array to an EmailsList object, so that I can give it methods, and I can't see how to do this, since the create method returns individual emails, not the whole emails property.
For that behaviour you can add ignore on the emails propery and let the mapping plugin serperate map the emails into a standar array that you feed the EmailsList constructor with.
var emailMapping = {
create: function(opt) {
return new Email(opt.data);
}
};
this.emails = new EmailList(ko.mapping.fromJS(arr, emailMapping));
Hmm, you could also extend a custom class with a observableArray I think (havent tested) that ko.mapping will then just add emails to your EmailList like
this.emails = new EmailList(); //This is is a ko.observableArray the mapping plugin will populate it with emails
ko.mapping.fromJS(data, mappingInfo, this);
https://github.com/knockout/knockout/blob/master/src/subscribables/observableArray.js
update: I just confirmed that solution two worked, so I would say thats a good way of doing it
http://jsfiddle.net/xop2go2z/
//stolen from ko source
function setPrototypeOf(obj, proto) {
obj.__proto__ = proto;
return obj;
}
ko.MyList = function(arr) {
var result = ko.observableArray(arr);
return setPrototypeOf(result, ko.MyList.fn);
}
ko.MyList.fn = {
pushMyThingy: function(obj) {
this.push(obj);
}
};

Resources