Ember Data Serializer & Adapter with Supabase returns empty (Proxy { }) - ember.js

Struggling to create my customised adapter & serializer to integrate Supabase, how I'm stuck why no data in Ember Data.
Trying out with a simple findAll() method. See below:
Service ⬇️:
export default class SupabaseService extends Service {
client;
constructor() {
super(...arguments);
const { url, key } = ENV.supabase;
const supabase = createClient(url, key);
this.client = supabase;
}
}
Model ⬇️:
export default class CourseModel extends Model {
#attr('string') name;
#attr('date') date_added;
}
Adapter ⬇️:
export default class ApplicationAdapter extends RESTAdapter {
#service supabase;
async findAll(store, type, neverSet, snapshotRecordArray) {
return new Promise(async (resolve, reject) => {
try {
const { data, error, status } = await this.supabase.client
.from(pluralize(type.modelName))
.select('*');
if (error) {
reject(error);
} else {
resolve(data);
}
} catch (error) {
reject(error);
}
});
}
}
Serializer ⬇️:
normalizeResponse(store, primaryModelClass, payload, id, requestType) {
// parse the response data from the server and return it in the format that Ember Data expects
let newPayload = {
data: payload.map(item => {
let attributes = JSON.parse(JSON.stringify(item));
delete attributes.id;
return {
id: item.id,
type: primaryModelClass.modelName,
attributes: attributes
}
})
}
return super.normalizeResponse(store, primaryModelClass, newPayload, id, requestType);
}
✅ The service works fine. The adapter manage to get data and returns as follows:
[
{
"id": "259f46fd-3321-4cc9-ad5e-6d6ec880f7f1",
"date_added": "2022-12-31T00:03:14.618585+00:00",
"name": "Science"
},
{
"id": "62a6a085-604b-4600-8cc4-59a8c9af284a",
"date_added": "2022-12-31T00:03:30.010963+00:00",
"name": "Physics"
}
]
The serializer newPayload to follow JSON API schema, returns:
{
"data": [
{
"id": "259f46fd-3321-4cc9-ad5e-6d6ec880f7f1",
"type": "course",
"attributes": {
"name": "Science",
"date_added": "2022-12-31T00:03:14.618585+00:00"
}
},
{
"id": "62a6a085-604b-4600-8cc4-59a8c9af284a",
"type": "course",
"attributes": {
"name": "Physics",
"date_added": "2022-12-31T00:03:30.010963+00:00"
}
}
]
}
But the problem is no data in store. Logging model in template shows empty Proxy {}.
I have no idea why. Ember Inspector shows no model in Data.
Any suggestions?

Related

BatchWriteItemCommand with AWS.DynamoDB class using AWS SDK V3 in Nodejs

I have been trying for hours to perform a DynamoDB DeleteRequest using BatchWriteItemCommand but I keep getting the following error:
Error ValidationException: 1 validation error detected: Value null at 'requestItems.td_notes_sdk.member.1.member.deleteRequest.key' failed to satisfy constraint: Member must not be null
This is what my table looks like:
Partition key: user_id (string)
Sort key: timestamp (number)
DynamoDB Screenshot
This is what my code looks like:
// Import required AWS SDK clients and commands for Node.js
import {
DynamoDBClient,
BatchWriteItemCommand,
} from "#aws-sdk/client-dynamodb";
// Set the parameters
export const params = {
RequestItems: {
"td_notes_sdk": [
{
DeleteRequest: {
Item: {
Key: {
user_id: { S : "bb" },
timestamp: { N : 2 },
},
},
},
},
],
},
};
export const run = async () => {
const ddbClient = new DynamoDBClient({ region: "us-east-2" });
try {
const data = await ddbClient.send(new BatchWriteItemCommand(params));
console.log("Success, items inserted", data);
return data;
} catch (err) {
console.log("Error", err);
}
};
run();
Here are some resources that I've been trying to follow along with:
Resource 1: Writing items in Batch Example
Resource 2: AWS Javascript SDK v3 Documentation
Update: BatchWrite PutRequest work with the code below, so I know that the structure of my keys/attributes is closer to being correct. Still does not work for DeleteRequest.
export const params = {
RequestItems: {
"td_notes_sdk": [
{
PutRequest: {
Item: {
user_id: { "S": "bb" },
timestamp: { "N": "5" },
},
},
},
],
},
};
You don't supply an Item when deleting an item. You supply a Key.
Here is a working example:
const params_delete = {
RequestItems: {
"td_notes_sdk": [
{
DeleteRequest: {
Key: {
user_id: { S: "bb" },
timestamp: { N: "2" },
},
},
},
],
},
};
const delete_batch = async () => {
const ddbClient = new DynamoDBClient({ region: "us-east-2" });
try {
const data = await ddbClient.send(new BatchWriteItemCommand(params_delete));
console.log("Success, item deleted");
return data;
} catch (err) {
console.log("Error", err);
}
};
delete_batch();

Apollo Server - how to create and add object in resolver when type isn't available in namespace

In the following example, I am attempting to create a post and add it to the Dictionary 'post'. How is the Mutation expected to create, add to the hash, and return the type of the item created when the item type isn't available to the namespace of the resolver?
mutation createPost {
createPost(input: {name: "Post Name"}){
name
}
}
index.js:
const { ApolloServer, gql } = require('apollo-server');
const dictionary = {};
const typeDefs = gql`
input PostSpecInput {
name: String
}
type PostSpec {
id: ID!
name: String
}
type Mutation {
createPost(input: PostSpecInput): PostSpec
}
type Query {
post_specs: [PostSpec]
}
`;
const resolvers = {
Query: {
post_specs: () => Object.keys(dictionary).map(function(key){
return dictionary[key];
})
},
Mutation: {
createPost(parent, args, context, info) {
var id = require('crypto').randomBytes(10).toString('hex');
const postSpec = new PostSpec(id, args.input);
posts_mock_database[id] = args.input;
return postSpec;
}
}
}
const server = new ApolloServer({typeDefs, resolvers})
server.listen().then(({url}) => {
console.log(`Server Ready at ${url}`);
})
Error:
{
"errors": [
{
"message": "PostSpec is not defined",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"createPost"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"ReferenceError: PostSpec is not defined",
" at createPost (index.js:38:34)",
Type definitions are not classes nor object instances, they are just for enforcing type. Even if you were in the namespace, calling 'new' would not work. Here is the solution for your mock database:
Mutation: {
createPost(parent, args, context, info) {
var id = require('crypto').randomBytes(10).toString('hex');
const newPostSpec = { id: id, name: args.input.name }
posts_mock_database[id] = newPostSpec;
return newPostSpec;
}
}

Customize JSON collection name in Serializer

When accessing API resources like /api/users/ which list/search a resource Ember Data expectects the response to be in this format:
{
"users": [{
"name": "Rails"
}, {
"name": "Omakase"
}]
}
but my response looks like this:
{
"results": [{
"name": "Rails"
}, {
"name": "Omakase"
}]
}
How can I tell my Serializer to turn results into users?
The following Serialzer renames single attributes but not the whole list as shown above:
import DS from 'ember-data';
export default DS.ActiveModelSerializer.extend({
attrs: {
"users" : "results",
}
});
this should do it for you, dont change attributes, just these two methods for extracting single models and arrays of models. You take the payload.results property and process it further, instead of the default payload.
extractArray: function(store, type, payload) {
return Array.prototype.map.call(payload.results, function(hash) {
return this.normalize(type, hash, payload.type);
}, this);
},
extractSingle: function(store, type, payload, recordId) {
return this.normalize(type, payload.results, payload.type);
}

Ember get hasMany

I have Model
EmberApp.Card = DS.Model.extend({
balance: DS.attr('number'),
operations: DS.hasMany('operation', { async: true })
});
Than I make
self.store.find('card', 'me')
response
{
"card": {
"id": "53620486168e3e581cb5851a",
"balance": 20
}
}
getting card Model but without operations and setting it to controller prop "currentCard"
Then I want to find operations throw url /cards/me/operations
response
{"operation": [{ "id": 1, "type": 1 }] }
How can I do it from this.controllerFor('*').get('currentCard')... ?
You actually need to return your operations as a list of ID's in your card response like this:
{
"card": {
"id": "53620486168e3e581cb5851a",
"balance": 20,
"operations": [1, 2, 3]
}
}
This will enable you to do this in another route:
this.modelFor('card').get('operations');
or this in your cards controller:
this.get('content.operations');
This will execute the following call to your API:
/api/operations?ids[]=1&ids[]=2&ids[]=3
First of all you should add links to response from cards/me
EmberApp.ApplicationSerializer = DS.RESTSerializer.extend({
normalizePayload: function(type, payload) {
if (type.toString() === 'EmberApp.Card') {
payload.links = { 'operations': '/cards/me/operations' };
return { card: payload };
} else if (type.toString() === 'EmberApp.Operation') {
return { operations: payload };
}
}
});
Then in route
EmberApp.CardOperationsRoute = Ember.Route.extend({
model: function() {
return this.controllerFor('sessions.new')
.get('currentCard')
.get('operations');
}
});

Ember.js and API pagination

I'm using Ember.js (v1.2.0) with an API which returns paginated JSON data like this:
{
"count": 5,
"next": "http://127.0.0.1:8000/some/resource/?page=2",
"previous": null,
"results": [
{
"id": 37,
"title": "Some title",
"description": "Some description",
},
{
"id": 35,
"title": "Sdflskdf",
"description": "sdfkdsjf",
},
{
"id": 34,
"title": "Some other title",
"description": "Dsdlfksdf",
},
]
}
I'm not using ember-data, so I'm using a plain ember object as my model and loading the data like this:
App.SomeResource = Ember.Object.extend({});
App.SomeResource.reopenClass({
find: function () {
return $.getJSON('/some/resource/').then(function (response) {
return response.results.map(function (data) {
return App.SomeResource.create(data);
});
});
},
});
The find method on my model class returns a promise which resolves to an array of objects. While creates SomeResource objects, all the pagination data is lost.
Is there a way to store count, next and previous page urls somewhere when the promise resolves?
I am assigning them to global object but you should do better.
App.SomeResource = Ember.Object.extend({});
App.SomeResource.reopenClass({
find: function () {
return $.getJSON('/some/resource/').then(function (response) {
return RSVP.all(response.results.map(function (data) {
return App.SomeResource.create(data);
})).then(function(createdResources) {
window.count = response.count;
window.next = response.next;
window.previous = response.previous;
return createdResources;
});
});
}
});
Rather than storing this metadata on the global window object, I came up with this:
App.SomeResource.reopenClass({
find: function () {
var url = '/some/resource/';
return Ember.$.getJSON(url).then(function (response) {
response.results = response.results.map(function (resource) {
return App.SomeResource.create(resource);
});
return response;
});
},
});
SomeResource.find() just instantiates Ember objects from the results array and then returns the response with the rest of the data untouched. The route then receives the response and sets up the pagination data and the model in the setupController function:
App.SomeResourceRoute = Ember.Route.extend({
model: function () {
return App.SomeResource.find();
},
setupController: function(controller, model) {
controller.setProperties({count: model.count,
pageCount: model.page_count,
currentPage: model.current_page,
pageRange: model.page_range,
previous: model.previous,
next: model.next,
model: model.results,});
},
});
It works, but maybe there is a better way.