Skip to content

Commit 8ac8fb6

Browse files
authored
Merge pull request #64 from calebuharrison/ch/keycasing-for-server-calls
Add support for keyCasing for server calls
2 parents 74ee9ad + 0393731 commit 8ac8fb6

2 files changed

Lines changed: 155 additions & 3 deletions

File tree

src/scope.ts

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ export class Scope<T extends SpraypaintBase = SpraypaintBase> {
8585
const copy = this.copy()
8686

8787
Object.keys(obj).forEach(k => {
88-
copy._associations[k] = (obj as any)[k]
88+
const serverCasedKey = this.model.serializeKey(k)
89+
copy._associations[serverCasedKey] = (obj as any)[k]
8990
})
9091

9192
return copy
@@ -107,6 +108,7 @@ export class Scope<T extends SpraypaintBase = SpraypaintBase> {
107108

108109
where(clause: WhereClause): Scope<T> {
109110
const copy = this.copy()
111+
clause = this._serverCasedWhereClause(clause)
110112

111113
for (const key in clause) {
112114
if (clause.hasOwnProperty(key)) {
@@ -129,6 +131,7 @@ export class Scope<T extends SpraypaintBase = SpraypaintBase> {
129131

130132
stats(clause: StatsScope): Scope<T> {
131133
const copy = this.copy()
134+
clause = this._serverCasedStatsClause(clause)
132135

133136
for (const key in clause) {
134137
if (clause.hasOwnProperty(key)) {
@@ -140,6 +143,7 @@ export class Scope<T extends SpraypaintBase = SpraypaintBase> {
140143

141144
order(clause: SortScope | string): Scope<T> {
142145
const copy = this.copy()
146+
clause = this._serverCasedOrderClause(clause)
143147

144148
if (typeof clause === "object") {
145149
for (const key in clause) {
@@ -156,6 +160,7 @@ export class Scope<T extends SpraypaintBase = SpraypaintBase> {
156160

157161
select(clause: FieldArg) {
158162
const copy = this.copy()
163+
clause = this._serverCasedFieldsClause(clause)
159164

160165
if (Array.isArray(clause)) {
161166
let _clause = clause as string[]
@@ -174,6 +179,7 @@ export class Scope<T extends SpraypaintBase = SpraypaintBase> {
174179

175180
selectExtra(clause: FieldArg) {
176181
const copy = this.copy()
182+
clause = this._serverCasedFieldsClause(clause)
177183

178184
if (Array.isArray(clause)) {
179185
let _clause = clause as string[]
@@ -192,6 +198,7 @@ export class Scope<T extends SpraypaintBase = SpraypaintBase> {
192198

193199
includes(clause: IncludeScope): Scope<T> {
194200
const copy = this.copy()
201+
clause = this._serverCasedIncludesClause(clause)
195202

196203
const directive = new IncludeDirective(clause)
197204
const directiveObject = directive.toScopeObject()
@@ -348,4 +355,53 @@ export class Scope<T extends SpraypaintBase = SpraypaintBase> {
348355

349356
return new CollectionProxy(recordArray, jsonResult)
350357
}
358+
359+
private _serverCasedWhereClause(clause: WhereClause) {
360+
return this._serverCasedClause(clause, false)
361+
}
362+
363+
private _serverCasedOrderClause(clause: string | SortScope) {
364+
if (typeof clause === "string") {
365+
return this._serverCasedClause(clause, true)
366+
} else {
367+
return this._serverCasedClause(clause, false)
368+
}
369+
}
370+
371+
private _serverCasedFieldsClause(clause: FieldArg) {
372+
return this._serverCasedClause(clause, true)
373+
}
374+
375+
private _serverCasedIncludesClause(clause: IncludeScope) {
376+
return this._serverCasedClause(clause, true)
377+
}
378+
379+
private _serverCasedStatsClause(clause: StatsScope): StatsScope {
380+
return this._serverCasedClause(clause, true)
381+
}
382+
383+
private _serverCasedClause(thing: any, transformValues: boolean = false) {
384+
if (typeof thing === "string") {
385+
return transformValues ? this.model.serializeKey(thing) : thing
386+
} else if (thing instanceof Array) {
387+
return thing.map(
388+
(item: any): any => this._serverCasedClause(item, transformValues)
389+
)
390+
} else if (thing instanceof Object) {
391+
let serverCasedThing = {}
392+
for (const property in thing) {
393+
if (thing.hasOwnProperty(property)) {
394+
const serverCasedProperty = this.model.serializeKey(property)
395+
const serverCasedPropertyValue = this._serverCasedClause(
396+
thing[property],
397+
transformValues
398+
)
399+
serverCasedThing[serverCasedProperty] = serverCasedPropertyValue
400+
}
401+
}
402+
return serverCasedThing
403+
} else {
404+
return thing
405+
}
406+
}
351407
}

test/unit/scope.test.ts

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ let scope: Scope<Author>
88
beforeEach(() => {
99
const model = sinon.stub() as any
1010
model.jsonapiType = "people"
11+
model.serializeKey = SpraypaintBase.serializeKey
12+
model.keyCase = { server: "snake", client: "camel" }
1113
scope = new Scope(model)
1214
})
1315

@@ -55,6 +57,14 @@ describe("Scope", () => {
5557
expect(newScope).to.be.instanceof(Scope)
5658
expect(newScope).not.to.equal(scope)
5759
})
60+
61+
it("respects the server keycase configuration", () => {
62+
scope = scope.where({ fooBar: "baz" }).where({ fooBarBaz: "fooBar" })
63+
expect((scope as any)._filter).to.eql({
64+
foo_bar: "baz",
65+
foo_bar_baz: "fooBar"
66+
})
67+
})
5868
})
5969

6070
describe("#stats()", () => {
@@ -72,6 +82,13 @@ describe("Scope", () => {
7282
expect(newScope).to.be.instanceof(Scope)
7383
expect(newScope).not.to.equal(scope)
7484
})
85+
86+
it("respects the server keycase configuration", () => {
87+
scope = scope.stats({ someThing: "someOtherThing" })
88+
expect((scope as any)._stats).to.eql({
89+
some_thing: "some_other_thing"
90+
})
91+
})
7592
})
7693

7794
describe("#order()", () => {
@@ -88,6 +105,14 @@ describe("Scope", () => {
88105
expect(newScope).to.be.instanceof(Scope)
89106
expect(newScope).not.to.equal(scope)
90107
})
108+
109+
it("respects the server keycase configuration", () => {
110+
scope = scope.order("fooBar").order({ fooBarBaz: "desc" })
111+
expect((scope as any)._sort).to.eql({
112+
foo_bar: "asc",
113+
foo_bar_baz: "desc"
114+
})
115+
})
91116
})
92117

93118
describe("#select()", () => {
@@ -107,6 +132,13 @@ describe("Scope", () => {
107132
expect(newScope).not.to.equal(scope)
108133
})
109134

135+
it("respects the server keycase configuration", () => {
136+
scope = scope.select({ someThing: ["firstThing", "secondThing"] })
137+
expect((scope as any)._fields).to.eql({
138+
some_thing: ["first_thing", "second_thing"]
139+
})
140+
})
141+
110142
describe("when passed an array of strings", () => {
111143
it("infers the jsonapi type", () => {
112144
const newScope = scope.select(["foo"])
@@ -134,6 +166,13 @@ describe("Scope", () => {
134166
expect(newScope).not.to.equal(scope)
135167
})
136168

169+
it("respects the server keycase configuration", () => {
170+
scope = scope.selectExtra({ someThing: ["firstThing", "secondThing"] })
171+
expect((scope as any)._extra_fields).to.eql({
172+
some_thing: ["first_thing", "second_thing"]
173+
})
174+
})
175+
137176
describe("when passed an array of strings", () => {
138177
it("infers the jsonapi type", () => {
139178
const newScope = scope.selectExtra(["foo"])
@@ -152,6 +191,13 @@ describe("Scope", () => {
152191
foo: {}
153192
})
154193
})
194+
195+
it("respects the server keycase configuration", () => {
196+
scope = scope.includes("fooBar")
197+
expect((scope as any)._include).to.eql({
198+
foo_bar: {}
199+
})
200+
})
155201
})
156202

157203
describe("when passed an array", () => {
@@ -162,6 +208,14 @@ describe("Scope", () => {
162208
bar: {}
163209
})
164210
})
211+
212+
it("respects the server keycase configuration", () => {
213+
scope = scope.includes(["fooBar", "fooBarBaz"])
214+
expect((scope as any)._include).to.eql({
215+
foo_bar: {},
216+
foo_bar_baz: {}
217+
})
218+
})
165219
})
166220

167221
describe("when passed a nested object", () => {
@@ -176,6 +230,20 @@ describe("Scope", () => {
176230
}
177231
})
178232
})
233+
234+
it("respects the server keycase configuration", () => {
235+
scope = scope.includes({
236+
fooBar: ["fooBarBaz", { fizzBuzz: "fizzBuzzBar" }]
237+
})
238+
expect((scope as any)._include).to.eql({
239+
foo_bar: {
240+
foo_bar_baz: {},
241+
fizz_buzz: {
242+
fizz_buzz_bar: {}
243+
}
244+
}
245+
})
246+
})
179247
})
180248

181249
it("returns a new scope", () => {
@@ -185,6 +253,34 @@ describe("Scope", () => {
185253
})
186254
})
187255

256+
describe("#merge()", () => {
257+
it("updates the scope", () => {
258+
scope = scope
259+
.includes(["foo_bar"])
260+
.merge({ foo_bar: scope.where({ foo: "bar" }) })
261+
const qp = scope.asQueryParams()
262+
263+
expect(qp.filter).to.eql({
264+
foo_bar: {
265+
foo: "bar"
266+
}
267+
})
268+
})
269+
270+
it("respects the server keycase configuration", () => {
271+
scope = scope
272+
.includes(["foo_bar_baz"])
273+
.merge({ fooBarBaz: scope.where({ foo: "bar" }) })
274+
const qp = scope.asQueryParams()
275+
276+
expect(qp.filter).to.eql({
277+
foo_bar_baz: {
278+
foo: "bar"
279+
}
280+
})
281+
})
282+
})
283+
188284
describe("#scope()", () => {
189285
it("returns itself", () => {
190286
expect(scope.scope()).to.equal(scope)
@@ -239,14 +335,14 @@ describe("Scope", () => {
239335
scope = scope
240336
.page(2)
241337
.per(10)
242-
.where({ foo: "bar" })
338+
.where({ fooBar: "baz" })
243339
.order("foo")
244340
.order({ bar: "desc" })
245341
.select({ people: ["name", "age"] })
246342
.stats({ total: "count" })
247343
.includes({ a: ["b", { c: "d" }] })
248344
expect(scope.toQueryParams()).to.eq(
249-
"page[number]=2&page[size]=10&filter[foo]=bar&sort=foo,-bar&fields[people]=name,age&stats[total]=count&include=a.b,a.c.d"
345+
"page[number]=2&page[size]=10&filter[foo_bar]=baz&sort=foo,-bar&fields[people]=name,age&stats[total]=count&include=a.b,a.c.d"
250346
)
251347
})
252348

0 commit comments

Comments
 (0)