How to split schema in GraphQL without having circular dependencies? - javascript

My question is similar to Javascript circular dependency in GraphQL code but my problem is not on the structure and database level, but in javascript (ES6).
My schema definition is getting to grow too large but I don't see where I could cut the file into pieces. It seems to be logical to cut based on the different object types, but that brings to circular dependencies similarly to this very much simplified, non-working example:
// -- file A.js
import { bConnection, getBs } from 'B';
export class A { /*...*/ };
export var getA = (a) => { /*...*/ };
export var getAs = (array_of_as) => { /*...*/ };
export var aType = new GraphQLObjectType ({
name: 'A',
fields: () => ({
bs: {
type: bConnection,
/*...*/
},
resolve: (a, args) => connectionFromPromisedArray (
getBs (a.bs)
),
/*...*/
}),
interfaces: () => [ require ('./nodeDefs').nodeInterface ],
/*...*/
})
export var {
connectionType: aConnection,
edgeType: aEdge
} = connectionDefinitions ({
name: 'A',
nodeType: aType
});
// -- file B.js
import { aConnection, getAs } from 'A';
export class B { /*...*/ };
export var getB = (b) => { /*...*/ };
export var getBs = (array_of_bs) => { /*...*/ };
export var bType = new GraphQLObjectType ({
name: 'B',
fields: () => ({
as: {
type: aConnection,
/*...*/
},
resolve: (b, args) => connectionFromPromisedArray (
getAs (b.bs)
),
/*...*/
}),
interfaces: () => [ require ('./nodeDefs').nodeInterface ],
/*...*/
})
export var {
connectionType: bConnection,
edgeType: bEdge
} = connectionDefinitions ({
name: 'B',
nodeType: bType
});
// -- file nodeDefs.js
import {
fromGlobalId,
nodeDefinitions,
} from 'graphql-relay';
import { A, getA, aType } from 'A'
import { B, getB, bType } from 'B'
export var {nodeInterface, nodeField} = nodeDefinitions (
(globalId) => {
var {type, id} = fromGlobalId (globalId);
if (type === 'A') {
return getA (id);
} else if (type === 'B') {
return getB (id);
}
},
(obj) => {
if (obj instanceof A) {
return aType;
} else if (obj instanceof B) {
return bType;
}
}
)
// -- file schema.js
import {
GraphQLObjectType,
GraphQLSchema,
} from 'graphql';
import { nodeField } from './nodeDefs';
var queryType = new GraphQLObjectType ({
name: 'Query',
fields: () => ({
node: nodeField,
/*...*/
}),
});
Is there a common way or best practice for this?

I've the same problem. I think the cleaner solutions is about use gruntjs concat.
grunt.initConfig({
concat: {
js: {
src: ['lib/before.js', 'lib/*', 'lib/after.js'],
dest: 'schema.js',
}
}
});
With this config, you can split your schema in several files, creating a final schema.js.
before.js may be this way:
import {
GraphQLObjectType,
GraphQLInt,
GraphQLString,
GraphQLSchema,
GraphQLList,
GraphQLNonNull
} from 'graphql';
import db from '../models/index.js';
import Auth from '../classes/auth';
after.js may be this way:
const Schema = new GraphQLSchema({
query: Query,
mutation: Mutation
})
export default Schema;
The others files will contains Objects like:
const Funcionario = new GraphQLObjectType({
name: 'Funcionario',
description: 'This represent a Funcionario',
fields: () => {
return {
id: {
type: GraphQLInt,
resolve(funcionario, args) {
return funcionario.id;
}
},
CPF: {
type: GraphQLString,
resolve(funcionario, args) {
return funcionario.CPF;
}
},
nome: {
type: GraphQLString,
resolve(funcionario, args) {
return funcionario.nome;
}
},
sobrenome: {
type: GraphQLString,
resolve(funcionario, args) {
return funcionario.sobrenome;
}
},
sessions: {
type: new GraphQLList(Session),
resolve(funcionario, args) {
return funcionario.getSessions();
}
}
}
}
})

See https://github.com/francoisa/todo/tree/master/server/graphql/types
todoType.js has a reference to viewerType which is defined in viewerType.js
viewerType.js imports from todoType

Related

Quasar QSelect is not opening when performing AJAX call

I have been trying to create a simple auto complete using Quasar's select but I'm not sure if this is a bug or if I'm doing something wrong.
Problem
Whenever I click the QSelect component, it doesn't show the dropdown where I can pick the options from.
video of the problem
As soon as I click on the QSelect component, I make a request to fetch a list of 50 tags, then I populate the tags to my QSelect but the dropdown doesn't show.
Code
import type { PropType } from "vue";
import { defineComponent, h, ref } from "vue";
import type { TagCodec } from "#/services/api/resources/tags/codec";
import { list } from "#/services/api/resources/tags/actions";
import { QSelect } from "quasar";
export const TagAutoComplete = defineComponent({
name: "TagAutoComplete",
props: {
modelValue: { type: Array as PropType<TagCodec[]> },
},
emits: ["update:modelValue"],
setup(props, context) {
const loading = ref(false);
const tags = ref<TagCodec[]>([]);
// eslint-disable-next-line #typescript-eslint/ban-types
const onFilterTest = (val: string, doneFn: (update: Function) => void) => {
const parameters = val === "" ? {} : { title: val };
doneFn(async () => {
loading.value = true;
const response = await list(parameters);
if (val) {
const needle = val.toLowerCase();
tags.value = response.data.data.filter(
(tag) => tag.title.toLowerCase().indexOf(needle) > -1
);
} else {
tags.value = response.data.data;
}
loading.value = false;
});
};
const onInput = (values: TagCodec[]) => {
context.emit("update:modelValue", values);
};
return function render() {
return h(QSelect, {
modelValue: props.modelValue,
multiple: true,
options: tags.value,
dense: true,
optionLabel: "title",
optionValue: "id",
outlined: true,
useInput: true,
useChips: true,
placeholder: "Start typing to search",
onFilter: onFilterTest,
"onUpdate:modelValue": onInput,
loading: loading.value,
});
};
},
});
What I have tried
I have tried to use the several props that is available for the component but nothing seemed to work.
My understanding is that whenever we want to create an AJAX request using QSelect we should use the onFilter event emitted by QSelect and handle the case from there.
Questions
Is this the way to create a Quasar AJAX Autocomplete? (I have tried to search online but all the answers are in Quasar's forums that are currently returning BAD GATEWAY)
What am I doing wrong that it is not displaying the dropdown as soon as I click on the QSelect?
It seems updateFn may not allow being async. Shift the async action a level up to solve the issue.
const onFilterTest = async (val, update /* abort */) => {
const parameters = val === '' ? {} : { title: val };
loading.value = true;
const response = await list(parameters);
let list = response.data.data;
if (val) {
const needle = val.toLowerCase();
list = response.data.data.filter((x) => x.title.toLowerCase()
.includes(needle));
}
update(() => {
tags.value = list;
loading.value = false;
});
};
I tested it by the following code and mocked values.
// import type { PropType } from 'vue';
import { defineComponent, h, ref } from 'vue';
// import type { TagCodec } from "#/services/api/resources/tags/codec";
// import { list } from "#/services/api/resources/tags/actions";
import { QSelect } from 'quasar';
export const TagAutoComplete = defineComponent({
name: 'TagAutoComplete',
props: {
modelValue: { type: [] },
},
emits: ['update:modelValue'],
setup(props, context) {
const loading = ref(false);
const tags = ref([]);
const onFilterTest = async (val, update /* abort */) => {
// const parameters = val === '' ? {} : { title: val };
loading.value = true;
const response = await new Promise((resolve) => {
setTimeout(() => {
resolve({
data: {
data: [
{
id: 1,
title: 'Vue',
},
{
id: 2,
title: 'Vuex',
},
{
id: 3,
title: 'Nuxt',
},
{
id: 4,
title: 'SSR',
},
],
},
});
}, 3000);
});
let list = response.data.data;
if (val) {
const needle = val.toLowerCase();
list = response.data.data.filter((x) => x.title.toLowerCase()
.includes(needle));
}
update(() => {
tags.value = list;
loading.value = false;
});
};
const onInput = (values) => {
context.emit('update:modelValue', values);
};
return function render() {
return h(QSelect, {
modelValue: props.modelValue,
multiple: true,
options: tags.value,
dense: true,
optionLabel: 'title',
optionValue: 'id',
outlined: true,
useInput: true,
useChips: true,
placeholder: 'Start typing to search',
onFilter: onFilterTest,
'onUpdate:modelValue': onInput,
loading: loading.value,
});
};
},
});

Angular Firebase integration in template

I'm brand new to Angular and typescript and still trying to make it through but now I can't.
I bought a template for Angular (VEX) and I would like to integrate data from firebase into a datatable already present in the template.
In the template, this table is fed by static data and I would like to replace that with my call to firebase.
I am really lost and I would like to understand how I can do to get there.
Here is the manage-users-component.ts
import { AfterViewInit, Component, Input, OnInit, ViewChild } from '#angular/core';
import { Observable, of, ReplaySubject } from 'rxjs';
import { filter } from 'rxjs/operators';
import { Customer } from './interfaces/customer.model';
import { MatTableDataSource } from '#angular/material/table';
import { MatPaginator } from '#angular/material/paginator';
import { MatSort } from '#angular/material/sort';
import { MatDialog } from '#angular/material/dialog';
import { TableColumn } from '../../../#vex/interfaces/table-column.interface';
import { aioTableData, aioTableLabels } from '../../static-data/aio-table-data';
import { CustomerCreateUpdateComponent } from './customer-create-update/customer-create-update.component';
import icEdit from '#iconify/icons-ic/twotone-edit';
import icDelete from '#iconify/icons-ic/twotone-delete';
import icSearch from '#iconify/icons-ic/twotone-search';
import icAdd from '#iconify/icons-ic/twotone-add';
import icFilterList from '#iconify/icons-ic/twotone-filter-list';
import { SelectionModel } from '#angular/cdk/collections';
import icMoreHoriz from '#iconify/icons-ic/twotone-more-horiz';
import icFolder from '#iconify/icons-ic/twotone-folder';
import { fadeInUp400ms } from '../../../#vex/animations/fade-in-up.animation';
import { MAT_FORM_FIELD_DEFAULT_OPTIONS, MatFormFieldDefaultOptions } from '#angular/material/form-field';
import { stagger40ms } from '../../../#vex/animations/stagger.animation';
import { FormControl } from '#angular/forms';
import { UntilDestroy, untilDestroyed } from '#ngneat/until-destroy';
import { MatSelectChange } from '#angular/material/select';
import icPhone from '#iconify/icons-ic/twotone-phone';
import icMail from '#iconify/icons-ic/twotone-mail';
import icMap from '#iconify/icons-ic/twotone-map';
import firebase from 'firebase';
import { UserManageService } from '../../services/user-manage.service';
#UntilDestroy()
#Component({
selector: 'vex-manage-users',
templateUrl: './manage-users.component.html',
styleUrls: ['./manage-users.component.scss'],
animations: [
fadeInUp400ms,
stagger40ms
],
providers: [
{
provide: MAT_FORM_FIELD_DEFAULT_OPTIONS,
useValue: {
appearance: 'standard'
} as MatFormFieldDefaultOptions
}
]
})
export class ManageUsersComponent implements OnInit, AfterViewInit {
layoutCtrl = new FormControl('boxed');
/**
* Simulating a service with HTTP that returns Observables
* You probably want to remove this and do all requests in a service with HTTP
*/
subject$: ReplaySubject<Customer[]> = new ReplaySubject<Customer[]>(1);
data$: Observable<Customer[]> = this.subject$.asObservable();
customers: Customer[];
#Input()
columns: TableColumn<Customer>[] = [
{ label: 'Checkbox', property: 'checkbox', type: 'checkbox', visible: true },
{ label: 'ShipTo', property: 'extId', type: 'text', visible: true },
{ label: 'uid', property: 'uid', type: 'text', visible: true },
{ label: 'Compagny', property: 'compagny', type: 'text', visible: true },
{ label: 'Name', property: 'name', type: 'text', visible: true, cssClasses: ['font-medium'] },
{ label: 'First Name', property: 'firstName', type: 'text', visible: false },
{ label: 'Last Name', property: 'lastName', type: 'text', visible: false },
{ label: 'Email', property: 'email', type: 'text', visible: true },
{ label: 'Phone', property: 'phone', type: 'text', visible: true },
{ label: 'Role', property: 'role', type: 'text', visible: true },
{ label: 'Actions', property: 'actions', type: 'button', visible: true }
];
pageSize = 10;
pageSizeOptions: number[] = [5, 10, 20, 50];
dataSource: MatTableDataSource<Customer> | null;
selection = new SelectionModel<Customer>(true, []);
searchCtrl = new FormControl();
labels = aioTableLabels;
icPhone = icPhone;
icMail = icMail;
icMap = icMap;
icEdit = icEdit;
icSearch = icSearch;
icDelete = icDelete;
icAdd = icAdd;
icFilterList = icFilterList;
icMoreHoriz = icMoreHoriz;
icFolder = icFolder;
#ViewChild(MatPaginator, { static: true }) paginator: MatPaginator;
#ViewChild(MatSort, { static: true }) sort: MatSort;
constructor(private dialog: MatDialog,
private usersManageService: UserManageService ) {
}
get visibleColumns() {
return this.columns.filter(column => column.visible).map(column => column.property);
}
/**
* Example on how to get data and pass it to the table - usually you would want a dedicated service with a HTTP request for this
* We are simulating this request here.
*/
getData() {
return of(aioTableData.map(customer => new Customer(customer)));
}
ngOnInit() {
const users = this.usersManageService.getUsers();
console.log(users);
this.getData().subscribe(customers => {
this.subject$.next(customers);
});
console.log(aioTableData);
this.dataSource = new MatTableDataSource();
this.data$.pipe(
filter<Customer[]>(Boolean)
).subscribe(customers => {
this.customers = customers;
this.dataSource.data = customers;
});
this.searchCtrl.valueChanges.pipe(
untilDestroyed(this)
).subscribe(value => this.onFilterChange(value));
}
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
this.dataSource.sort = this.sort;
}
createCustomer() {
this.dialog.open(CustomerCreateUpdateComponent).afterClosed().subscribe((customer: Customer) => {
/**
* Customer is the updated customer (if the user pressed Save - otherwise it's null)
*/
if (customer) {
/**
* Here we are updating our local array.
* You would probably make an HTTP request here.
*/
this.customers.unshift(new Customer(customer));
this.subject$.next(this.customers);
}
});
}
updateCustomer(customer: Customer) {
this.dialog.open(CustomerCreateUpdateComponent, {
data: customer
}).afterClosed().subscribe(updatedCustomer => {
/**
* Customer is the updated customer (if the user pressed Save - otherwise it's null)
*/
if (updatedCustomer) {
/**
* Here we are updating our local array.
* You would probably make an HTTP request here.
*/
const index = this.customers.findIndex((existingCustomer) => existingCustomer.uid === updatedCustomer.uid);
this.customers[index] = new Customer(updatedCustomer);
this.subject$.next(this.customers);
}
});
}
deleteCustomer(customer: Customer) {
/**
* Here we are updating our local array.
* You would probably make an HTTP request here.
*/
this.customers.splice(this.customers.findIndex((existingCustomer) => existingCustomer.uid === customer.uid), 1);
this.selection.deselect(customer);
this.subject$.next(this.customers);
}
deleteCustomers(customers: Customer[]) {
/**
* Here we are updating our local array.
* You would probably make an HTTP request here.
*/
customers.forEach(c => this.deleteCustomer(c));
}
onFilterChange(value: string) {
if (!this.dataSource) {
return;
}
value = value.trim();
value = value.toLowerCase();
this.dataSource.filter = value;
}
toggleColumnVisibility(column, event) {
event.stopPropagation();
event.stopImmediatePropagation();
column.visible = !column.visible;
}
/** Whether the number of selected elements matches the total number of rows. */
isAllSelected() {
const numSelected = this.selection.selected.length;
const numRows = this.dataSource.data.length;
return numSelected === numRows;
}
/** Selects all rows if they are not all selected; otherwise clear selection. */
masterToggle() {
this.isAllSelected() ?
this.selection.clear() :
this.dataSource.data.forEach(row => this.selection.select(row));
}
trackByProperty<T>(index: number, column: TableColumn<T>) {
return column.property;
}
// onLabelChange(change: MatSelectChange, row: Customer) {
// const index = this.customers.findIndex(c => c === row);
// this.customers[index].labels = change.value;
// this.subject$.next(this.customers);
// }
}
Here is the Firebase Realtime database "users" that I would like to insert in my datatable
Here is the function I imaginated for do the job but I dont know where to put it into
getUsers() {
firebase.database().ref('/users').once('value').then((snapshot) => {
const users = snapshot.val();
return users;
});
}
I am completely stuck and I thank very much in advance anyone who can help me.
I made some progress in my case and I was able to recover the values ​​I wanted.
The array returned by my function matches the array of the example template.
getData() {
const users = [];
firebase.database().ref('/users').once('value').then((snapshot) => {
users.push(snapshot.val()) ;
const users2 = users[0];
const mapped = Object.keys(users2).map(key => (users2[key]));
console.log(mapped.map(customer => new Customer(customer)));
// console.log(aioTableData.map(customer => new Customer(customer)));
return of(mapped.map(customer => new Customer(customer)));
});
// return of(aioTableData.map(customer => new Customer(customer)));
The problem is that in ngOnInit, when the call to the function is done, it expects a subscribe and I don't know how to do it.
ngOnInit() {
this.getData().subscribe(customers => {
this.subject$.next(customers);
});
this.dataSource = new MatTableDataSource();
this.data$.pipe(
filter<Customer[]>(Boolean)
).subscribe(customers => {
this.customers = customers;
this.dataSource.data = customers;
});
this.searchCtrl.valueChanges.pipe(
untilDestroyed(this)
).subscribe(value => this.onFilterChange(value));
}
An idea?

Jasmine test with selecting items from list

I have an angular application and I need to do some unit testing on some methods with Jasmine. IN this case I do a unit test on a select list. So that the select list will not be empty.
The method looks like this:
createStatusOptions(listValueoptions: OptionModel[], resources: any): OptionModel[] {
const processStatusOptions = listValueoptions.map(listValueOption => {
listValueOption.value = `${caseStatusEnum.inProgress}_${listValueOption.value}`;
listValueOption.selected = true;
return listValueOption;
});
const caseStatusEnumKeys = Object.keys(caseStatusEnum).filter(key => !isNaN(Number(key)));
const enumOptions = this.optionService.createOptions(
new ConfigOptionModel({ source: caseStatusEnumKeys, resources, resourcesModel: enumResourcesModel, isCustomEnum: true, }));
return [
this.getEnumOption(enumOptions, caseStatusEnum.submitted, true),
...processStatusOptions,
this.getEnumOption(enumOptions, caseStatusEnum.closed),
];
}
private getEnumOption(options: OptionModel[], enumType, isSelected = false): OptionModel {
const option = options.filter(enumOption => enumOption.value === `${enumType}`)[0];
option.selected = isSelected;
return option;
}
And I have the unit test like this:
it('should create status options when there ar list value options are provided', () => {
optionService.options = [
{
value: caseStatusEnum.submitted.toString(),
},
{
value: caseStatusEnum.inProgress.toString(),
},
{
value: caseStatusEnum.closed.toString(),
},
] as OptionModel[];
// tslint:disable-next-line:max-line-length
const result = service.createStatusOptions(optionService.options, [[103], [104], [105] ]);
console.log(result);
expect(result.length).toBe(2);
expect(result).toEqual([{ value: '103', selected: true }, { value: '105', selected: false }]);
});
But I get an error like this:
Services: CaseService > should create status options when there ar list value options are provided
TypeError: Cannot set property 'selected' of undefined
at <Jasmine>
at CaseService.getEnumOption (http://localhost:9878/src/app/case/src/services/case.service.ts?:130:9)
at CaseService.getEnumOption [as createStatusOptions] (http://localhost:9878/src/app/case/src/services/case.service.ts?:109:22)
at UserContext.<anonymous> (http://localhost:9878/src/app/case/src/services/case.service.spec.ts?:149:32)
at ZoneDelegate.../../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9878/E:/Projects/Source/Repos/VLR/Web/vlrworkspace/node_modules/zone.js/dist/zone.js?:388:1)
at ProxyZoneSpec.push.../../node_modules/zone.js/dist/proxy.js.ProxyZoneSpec.onInvoke (http://localhost:9878/E:/Projects/Source/Repos/VLR/Web/vlrworkspace/node_modules/zone.js/dist/proxy.js?:128:1)
at ZoneDelegate.../../node_modules/zone.js/dist/zone.js.ZoneDelegate.invoke (http://localhost:9878/E:/Projects/Source/Repos/VLR/Web/vlrworkspace/node_modules/zone.js/dist/zone.js?:387:1)
at Zone.../../node_modules/zone.js/dist/zone.js.Zone.run (http://localhost:9878/E:/Projects/Source/Repos/VLR/Web/vlrworkspace/node_modules/zone.js/dist/zone.js?:138:1)
at runInTestZone (http://localhost:9878/E:/Projects/Source/Repos/VLR/Web/vlrworkspace/node_modules/zone.js/dist/jasmine-patch.js?:145:1)
at UserContext.<anonymous> (http://localhost:9878/E:/Projects/Source/Repos/VLR/Web/vlrworkspace/node_modules/zone.js/dist/jasmine-patch.js?:160:1)
at <Jasmine>
So my question is: How to solve this?
Thank you
if I do this:
console.log(optionService.options);
I get this output:
Array(3)
0: {value: "103", selected: true}
1: {value: "104"}
2: {value: "105", selected: false}
length: 3
__proto__: Array(0)
this is the file:
import { fakeAsync, tick } from '#angular/core/testing';
import { FormServiceMock, MultiFileUploadServiceMock } from 'afw/forms/testing';
import { AfwHttp } from 'afw/generic-services';
import { AfwHttpMock, OptionServiceMock } from 'afw/generic-services/testing';
import { OptionModel, SearchResultModel } from 'afw/models';
import { FeedbackStoreServiceMock } from 'afw/store-services/testing';
import { RouterMock } from 'afw/testing';
import { PagingDataModel, TableSortDataModel } from 'afw/ui-components';
import { caseOwnerEnum, caseStatusEnum, caseTypeEnum, MultiFileUploadResourcesModel } from 'lr/models';
import { Observable, observable } from 'rxjs';
import { CaseTypeInfoModel } from 'support-shared/base/models';
import { CaseTypeInfoStoreServiceMock } from 'support-shared/base/services/case-type-info-store.service.mock';
import { CaseFormComponent } from '../case-base/src/case-form/case-form.component';
import { CaseBaseModel, CaseReferenceModel } from '../models';
import { CaseService } from './case.service';
let service: CaseService;
let afwHttpMock: AfwHttpMock;
// tslint:disable-next-line:prefer-const
let formServiceMock: FormServiceMock;
let multiFileUploadService: MultiFileUploadServiceMock;
let router: RouterMock;
let feedbackStoreService: FeedbackStoreServiceMock;
let optionService: OptionServiceMock;
let caseTypeInfoStoreService: CaseTypeInfoStoreServiceMock;
// tslint:disable-next-line:prefer-const
let component: CaseFormComponent;
fdescribe('Services: CaseService', () => {
beforeEach(() => {
afwHttpMock = new AfwHttpMock();
multiFileUploadService = new MultiFileUploadServiceMock();
router = new RouterMock();
feedbackStoreService = new FeedbackStoreServiceMock();
optionService = new OptionServiceMock();
caseTypeInfoStoreService = new CaseTypeInfoStoreServiceMock();
service = new CaseService(afwHttpMock as any, multiFileUploadService as any, router as any,
feedbackStoreService as any, optionService as any, caseTypeInfoStoreService as any);
});
it('should create an instance', () => {
expect(service).toBeTruthy();
});
it('should get case reference details', () => {
afwHttpMock.setupOnlyResponse({ type: caseTypeEnum.revisionRequest, details: { bsn: 'bsnLabel' } }, 200);
const d = service.getCaseReferenceDetails('spinnerMessage', { reference: '112314121', type: caseTypeEnum.revisionRequest });
d.subscribe(r => {
expect(r.details.length === 1);
expect(r.details[0].key).toBe('bsn');
expect(r.details[0].value).toBe('bsnLabel');
expect((r.details[0] as any).resourceKey).toBe('bsn');
});
afwHttpMock.returnSuccessResponse();
});
// tslint:disable-next-line:no-identical-functions
it('should get case reference details with full response', () => {
afwHttpMock.setupOnlyResponse({ body: { type: caseTypeEnum.revisionRequest, details: [{ key: 'hoi' }] } }, 200);
const d = service.getCaseReferenceDetailsFullResponse('spinnerMessage', { reference: '100001075', type: caseTypeEnum.revisionRequest });
// tslint:disable-next-line:no-commented-code
// tslint:disable-next-line:no-identical-functions
/* let result;
d.subscribe(r => {
result = r;
}); */
d.subscribe(r => {
expect(r.ok === true);
expect(r.body.details[0].key).toBe('hoi');
});
afwHttpMock.returnSuccessResponse();
// expect(result.ok === true);
// expect(result.)
});
// tslint:disable-next-line:no-commented-code
it('shoud get case type info configuration that is used on various views when snapshot exists', () => {
let result99: Observable<CaseTypeInfoModel[]>;
result99 = service.getCaseTypeInfo('spinner') as Observable<CaseTypeInfoModel[]>;
const response = [{ mock: 'mock' } as any];
service['caseTypeInfoSnapshot'] = response;
service.getCaseTypeInfo('spinner').subscribe(i => {
expect(i).toEqual(response);
});
});
// tslint:disable-next-line:no-identical-functions
it('shoud get case type info configuration that is used on various views when snapshot doesnt exists', () => {
let result99: Observable<CaseTypeInfoModel[]>;
const spy = spyOn(caseTypeInfoStoreService, 'addCaseTypeInfoToStore');
result99 = service.getCaseTypeInfo('spinner') as Observable<CaseTypeInfoModel[]>;
const response = [{ mock: 'mock' } as any];
service['caseTypeInfoSnapshot'] = response;
// caseTypeInfoStoreService..subscribe((result) => { expect(result).toBe(false); });
result99.subscribe((result) => {
expect(response).toEqual(response);
});
afwHttpMock.setupOnlyResponse(result99, 200);
afwHttpMock.returnSuccessResponse();
});
it('should create status options when no list value options are provided', () => {
optionService.options = [
{
value: caseStatusEnum.submitted.toString(),
},
{
value: caseStatusEnum.inProgress.toString(),
},
{
value: caseStatusEnum.closed.toString(),
},
] as OptionModel[];
// tslint:disable-next-line:no-commented-code
// const spy = spyOn(service, 'createStatusOptions');
const result = service.createStatusOptions([], {});
expect(result.length).toBe(2);
expect(result).toEqual([{ value: '103', selected: true }, { value: '105', selected: false }]);
// tslint:disable-next-line:no-commented-code
// const response = [{ mock: 'mock' } as any];
// expect(spy).toBe(result);
});
it('should create status options when there ar list value options are provided', () => {
optionService.options = [
{
value: caseStatusEnum.submitted.toString(),
},
{
value: caseStatusEnum.inProgress.toString(),
},
{
value: caseStatusEnum.closed.toString(),
},
] as OptionModel[];
// tslint:disable-next-line:max-line-length
const result = service.createStatusOptions(optionService.options, 103);
console.log(optionService.options);
expect(result.length).toBe(2);
expect(result).toEqual([{ value: '103', selected: true }, { value: '105', selected: false }]);
});
it('should get case reference without details', () => {
afwHttpMock.setupOnlyResponse({}, 200);
const spy = spyOn(afwHttpMock, 'post').and.callThrough();
const model = new CaseReferenceModel({ reference: '112314121', type: caseTypeEnum.revisionRequest });
const d = service.getCaseReferenceDetails('spinnerMessage', model);
d.subscribe(r => {
expect(r).toBeDefined();
});
expect(spy).toHaveBeenCalledWith('api/support/cases/get-reference-details', model, 'spinnerMessage');
afwHttpMock.returnSuccessResponse();
});
it('should add case reference without details', () => {
afwHttpMock.setupOnlyResponse({}, 200);
const spy = spyOn(afwHttpMock, 'post').and.callThrough();
const model = new CaseReferenceModel({ reference: '112314121', type: caseTypeEnum.revisionRequest });
const d = service.addCase('spinnerMessage', model as any);
d.subscribe(r => {
expect(r).toBeDefined();
});
expect(spy).toHaveBeenCalledWith('api/support/cases', model, 'spinnerMessage');
afwHttpMock.returnSuccessResponse();
});
it('should search for cases', () => {
const formModel: any = { makeQueryString: () => 'name=test' };
const pagingModel = new PagingDataModel({ currentPage: 10, itemsPerPage: 20 });
const sortModel = new TableSortDataModel({ columnName: 'kol', isDescending: false });
const spy = spyOn(afwHttpMock, 'get').and.callThrough();
const mockData = [
new CaseBaseModel({
id: 100000001,
type: caseTypeEnum.revisionRequest,
status: caseStatusEnum.inProgress,
substatus: 5266,
verdict: null,
owner: caseOwnerEnum.caseManager,
dateSubmitted: '02-02-2009',
dateClosed: '',
reference: 'aaa',
}),
];
const setupResponse = new SearchResultModel<CaseBaseModel>();
setupResponse.result = mockData;
setupResponse.totalResultCount = 27;
afwHttpMock.setupOnlyResponse(setupResponse, 200);
let response: SearchResultModel<CaseBaseModel>;
service.search(formModel, sortModel, pagingModel, 'spinnerText').subscribe(result => {
response = result;
});
afwHttpMock.returnOnlyResponse();
expect(spy).toHaveBeenCalledWith('api/support/cases?name=test&columnName=kol&isDescending=false&currentPage=10&itemsPerPage=20',
'spinnerText');
expect(response).toEqual(setupResponse);
expect(response.result[0].getResourceForStatus).toBeDefined();
});
it('should save documents', fakeAsync(() => {
const spy = spyOn(multiFileUploadService, 'syncFilesWithBackend').and.callThrough();
const spyRouter = spyOn(router, 'navigate').and.callThrough();
const spyFeedback = spyOn(feedbackStoreService, 'addSuccessMessageOnMainPortal');
service.saveDocuments(1, [{} as any], MultiFileUploadResourcesModel.keys, '../', { key: 'da', value: 'fa' });
expect(spy).toHaveBeenCalledWith('api/support/cases/1/documents', [{}],
MultiFileUploadResourcesModel.keys.bijlageToevoegenSpinnerTekst,
MultiFileUploadResourcesModel.keys.bijlageVerwijderenSpinnerTekst
);
tick();
expect(spyRouter).toHaveBeenCalledWith(['../']);
expect(spyFeedback).toHaveBeenCalled();
}));
it('should not save documents if there are no documents in array', fakeAsync(() => {
const spy = spyOn(multiFileUploadService, 'syncFilesWithBackend').and.callThrough();
const spyRouter = spyOn(router, 'navigate').and.callThrough();
const spyFeedback = spyOn(feedbackStoreService, 'addSuccessMessageOnMainPortal');
service.saveDocuments(1, [], MultiFileUploadResourcesModel.keys, '../', { key: 'da', value: 'fa' });
expect(spy).not.toHaveBeenCalled();
tick();
expect(spyRouter).toHaveBeenCalledWith(['../']);
expect(spyFeedback).toHaveBeenCalled();
}));
it('should save documents and report errors', fakeAsync(() => {
multiFileUploadService.setResponse([{}, { error: {} }]);
spyOn(multiFileUploadService, 'makeWarningMessageForUnsyncedFiles').and.returnValue('mock');
const spyRouter = spyOn(router, 'navigate').and.callThrough();
const spyFeedback = spyOn(feedbackStoreService, 'addWarningMessageOnMainPortal');
const spy = spyOn(multiFileUploadService, 'syncFilesWithBackend').and.callThrough();
service.saveDocuments(1, [{} as any], MultiFileUploadResourcesModel.keys, '../', { key: 'da', value: 'fa' });
expect(spy).toHaveBeenCalledWith('api/support/cases/1/documents', [{}],
MultiFileUploadResourcesModel.keys.bijlageToevoegenSpinnerTekst,
MultiFileUploadResourcesModel.keys.bijlageVerwijderenSpinnerTekst
);
tick();
expect(spyRouter).toHaveBeenCalledWith(['../']);
expect(spyFeedback).toHaveBeenCalled();
}));
it('should get case by id', () => {
const id = 66208014;
const setupResponse = new CaseBaseModel({
id,
dateSubmitted: '',
owner: caseOwnerEnum.caseManager,
reference: 'ksjhkjshdf',
status: caseStatusEnum.submitted,
type: caseTypeEnum.revisionRequest,
});
afwHttpMock.setupOnlyResponse(setupResponse, 200);
service.getCase(id, 'spinner').subscribe(r => {
expect(r).toEqual(setupResponse);
});
afwHttpMock.returnSuccessResponse();
});
it('edit the case with model', () => {
const spy = spyOn(service, 'editCase').and.callThrough();
const caseUpdate = new CaseBaseModel({
id: 100001075,
dateSubmitted: '',
owner: caseOwnerEnum.caseManager,
reference: 'ksjhkjshdf',
status: caseStatusEnum.submitted,
type: caseTypeEnum.revisionRequest,
});
service.editCase('spinner', caseUpdate);
expect(spy).toHaveBeenCalledWith('spinner', caseUpdate);
expect(caseUpdate.id).toEqual(100001075);
});
});
Based on what you showed so far, my guess is that the options parameter passed to getEnumOption() is undefined, which is causing the error you see. A quick console.log(options) within getEnumOption() would verify this.
If your code is working fine otherwise, but only failing in the test then I would make a second guess that you haven't properly mocked/spiedOn this.optionService.createOptions() since it sets up the options parameter that is potentially undefined. That would have been done earlier in the .spec file - if you post the whole file then that would help others who read your question to determine if this is the case.
Update with Stackblitz
I put all your code into a Stackblitz to test it. There was a lot of code I didn't have access to that I just guessed at the functionality of. However, I did discover a few things.
First, when you are testing you appear to be using the same variable both for the mock of the return expected by this.optionService.createOptions() as well as in the call to service.createStatusOptions() - which is likely not what you want to do.
Here is the code snippet I am talking about:
optionService.options = [
{
value: caseStatusEnum.submitted.toString(),
},
{
value: caseStatusEnum.inProgress.toString(),
},
{
value: caseStatusEnum.closed.toString(),
},
] as OptionModel[];
// tslint:disable-next-line:max-line-length
const result = service.createStatusOptions(optionService.options, [[103], [104], [105] ]);
When I called it this way in the Stackblitz I ran into a mutability issue - you are changing the data within the members of the objects inside the array, which will change it whereever that variable is accessed. To overcome this in the Stackblitz I made two copies of the data, one to use in the mock returnValue and another completely separate array of objects for the call to service.createStatusOptions(). Also, I am not familiar with the way you are mocking your service call, so I replaced it with a simple Jasmine spy in the Stackblitz.
Feel free to have a look at what I produced. Perhaps it will be helpful.

How to create a tree as extension for Vs code

I want to create a tree in VS code, but my problem is how to manually add a node to my tree. I am not sure from where to start. I tried to review all the projects that created a tree for VScode as an extension.
My problem is that I am not an expert in Typescript and the examples are not so clear or I am not sure how it is working.
Would you mind helping me to understand how to create the tree in VS code? My problem is with creating a node and then adding the node to tree.
I reviewed these projects:
vscode-code-outline
vscode-extension-samples
vscode-git-tree-compare
vscode-html-languageserver-bin
vscode-mock-debug
vscode-tree-view
Update1:
I managed to use "vscode-extension-samples" and generate the below code examples; now I don't know what I should do, or in other words, how to fill the tree. I tried to use mytree class to fill the data but it didn't work. Would you mind advising me what is next?
extension.ts
'use strict';
import * as vscode from 'vscode';
import { DepNodeProvider } from './nodeDependencies'
import { JsonOutlineProvider } from './jsonOutline'
import { FtpExplorer } from './ftpExplorer.textDocumentContentProvider'
import { FileExplorer } from './fileExplorer';
//mycode
import { SCCExplorer } from './sccExplorer';
export function activate(context: vscode.ExtensionContext) {
// Complete Tree View Sample
new FtpExplorer(context);
new FileExplorer(context);
//mycode
new SCCExplorer(context);
// Following are just data provider samples
const rootPath = vscode.workspace.rootPath;
const nodeDependenciesProvider = new DepNodeProvider(rootPath);
const jsonOutlineProvider = new JsonOutlineProvider(context);
vscode.window.registerTreeDataProvider('nodeDependencies', nodeDependenciesProvider);
vscode.commands.registerCommand('nodeDependencies.refreshEntry', () => nodeDependenciesProvider.refresh());
vscode.commands.registerCommand('nodeDependencies.addEntry', node => vscode.window.showInformationMessage('Successfully called add entry'));
vscode.commands.registerCommand('nodeDependencies.deleteEntry', node => vscode.window.showInformationMessage('Successfully called delete entry'));
vscode.commands.registerCommand('extension.openPackageOnNpm', moduleName => vscode.commands.executeCommand('vscode.open', vscode.Uri.parse(`https://www.npmjs.com/package/${moduleName}`)));
vscode.window.registerTreeDataProvider('jsonOutline', jsonOutlineProvider);
vscode.commands.registerCommand('jsonOutline.refresh', () => jsonOutlineProvider.refresh());
vscode.commands.registerCommand('jsonOutline.refreshNode', offset => jsonOutlineProvider.refresh(offset));
vscode.commands.registerCommand('jsonOutline.renameNode', offset => jsonOutlineProvider.rename(offset));
vscode.commands.registerCommand('extension.openJsonSelection', range => jsonOutlineProvider.select(range));
}
sccExplorer.ts
import * as vscode from 'vscode';
import * as path from 'path';
import * as fs from 'fs';
import * as mkdirp from 'mkdirp';
import * as rimraf from 'rimraf';
//#region Utilities
interface Entry {
uri: vscode.Uri,
type: vscode.FileType
}
//#endregion
export class FileSystemProvider implements vscode.TreeDataProvider<Entry> {
getTreeItem(element: Entry): vscode.TreeItem | Thenable<vscode.TreeItem> {
throw new Error("Method not implemented.");
}
onDidChangeTreeData?: vscode.Event<Entry>;
getChildren(element?: Entry): vscode.ProviderResult<Entry[]> {
throw new Error("Method not implemented.");
}
getParent?(element: Entry): vscode.ProviderResult<Entry> {
throw new Error("Method not implemented.");
}
private _onDidChangeFile: vscode.EventEmitter<vscode.FileChangeEvent[]>;
constructor() {
this._onDidChangeFile = new vscode.EventEmitter<vscode.FileChangeEvent[]>();
}
}
export class SCCExplorer {
private fileExplorer: vscode.TreeView<any>;
constructor(context: vscode.ExtensionContext) {
const treeDataProvider = new myTree().directories;
this.fileExplorer = vscode.window.createTreeView('scc_Explorer', { treeDataProvider });
vscode.commands.registerCommand('scc_Explorer.openFile', (resource) => this.openResource(resource));
}
private openResource(resource: vscode.Uri): void {
vscode.window.showTextDocument(resource);
}
}
export class myTree{
directories: any;
constructor()
{
this.directories = [
{
name: 'parent1',
child: [{
name: 'child1',
child: []
},
{
name: 'child2',
child: []
}]
},
{
name: 'parent2',
child: {
name: 'child1',
child: []
}
},
{
name: 'parent2',
child: [{
name: 'child1',
child: []
},
{
name: 'child2',
child: []
}]
}];
}
}
I finally got it working. It took me very long and I had it right all the time. My issue was I never did explicitly expand the items, so I would never see nested results and only the top level.
Basic working example
import * as vscode from "vscode";
export class OutlineProvider
implements vscode.TreeDataProvider<any> {
constructor(private outline: any) {
console.log(outline);
}
getTreeItem(item: any): vscode.TreeItem {
return new vscode.TreeItem(
item.label,
item.children.length > 0
? vscode.TreeItemCollapsibleState.Expanded
: vscode.TreeItemCollapsibleState.None
);
}
getChildren(element?: any): Thenable<[]> {
if (element) {
return Promise.resolve(element.children);
} else {
return Promise.resolve(this.outline);
}
}
}
export function activate(context: vscode.ExtensionContext) {
let disposable = vscode.commands.registerCommand(
"outliner.outline",
async () => {
vscode.window.registerTreeDataProvider(
"documentOutline",
new OutlineProvider([dataObject])
);
}
);
context.subscriptions.push(disposable);
}
const dataObject = {
label: "level one",
children: [
{
label: "level two a",
children: [
{
label: "level three",
children: [],
},
],
},
{
label: "level two b",
children: [],
},
],
}
And of course in package.json
"contributes": {
"commands": [
{
"command": "outliner.outline",
"title": "Outline"
}
],
"views": {
"explorer": [
{
"id": "documentOutline",
"name": "Document Outline"
}
]
}
},
Types
note the type for treeDataProvider is not neccecarly what you return. Only the getTree item has to return a tree item or a class that extends it.
interface CustomType {
label: string
children?: CustomType[]
}
export class TypeExample
implements vscode.TreeDataProvider<CustomType> {
constructor(private data: CustomType[]) { }
getTreeItem(element: CustomType): vscode.TreeItem {
return new vscode.TreeItem(
element.label,
(element.children?.length ?? 0) > 0
? vscode.TreeItemCollapsibleState.Expanded
: vscode.TreeItemCollapsibleState.None
);
}
getChildren(element?: CustomType): Thenable<CustomType[]> {
return element && Promise.resolve(element.children ?? [])
|| Promise.resolve(this.data);
}
}
I thought at first the type of the data provider should be the return type of the tree item, this doesnt make much sense of course and I was trying to wrap my head around the reasoning. Now I understand that you pass your custom type in and all other methods inherit this type and expect this type as its argument. Only the getTreeItem method has to return a valid tree item that can be rendered.

Data Binding with Smart table from json file

i want to bind the json file to a smart table. How to use the loop function for the iteration.. please help
It only shows the design of smart table.
didn't binding the data from json
this is the json file
[
{
"year": 2013,
"id": "",
"doctor": "Dr. Smith",
"illness": "Flu",
"apptdate": "3/12/2013",
"details":"Patient had flu for 5 days. No medicines prescribed"
}
]
i used to retrieve data using
#Injectable()
export class SmartTablesService {
constructor(private http: Http) {
}
smartTableData = [];
loadData() {
console.log('loadData');
this.http.get('http://192.168.0.100:8000/medical')
.subscribe((data) => {
setTimeout(() => {
var contactData = [];
$.each(data.json(), function (key, value) {
var tempData = value.source;
contactData.push(tempData);
});
this.smartTableData = contactData;
}, 1000);
});
}
getData(): Promise<any> {
console.log("Promise");
this.loadData();
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log(this.smartTableData);
resolve(this.smartTableData);
}, 3000);
});
}
}
constructor(private http: Http) { }
getComments() {
return this.http.get('http://192.168.0.100:8000/article' )
.map((res: Response) => res.json())
.catch((error:any) => Observable.throw(error));
}
}*/
this is the component part
#Component({
selector: 'new',
template: '<ng2-smart-table [settings]="settings" [source]="source"></ng2-smart-table>'
})
export class NewComponent {
query: string = '';
settings = {
noDataMessage: 'Loading...',
columns: {
year: {
title: 'YEAR',
type: 'string'
},
id: {
title: 'ID',
type: 'string'
},
doctor: {
title: 'DOCTOR',
type: 'string'
},
illness: {
title: 'ILLNESS',
type: 'string'
},
apptdate: {
title: 'APPTDATE',
type: 'string'
},
details: {
title: 'DETAILS',
type: 'string'
}
}
};
// data
source: LocalDataSource = new LocalDataSource();
constructor(protected service: SmartTablesService){
this.service.getData().then((data) => {
this.source.load(data);
});
}
}
please anyone anyone know how to bind it ..help
simply change the subscribe part in the service page to
var tempData = value;
so .subscriber looks like
.subscribe((data) => {
setTimeout(() => {
var contactData = [];
$.each(data.json(), function (key, value) {
var tempData = value; contactData.push(tempData);
});
this.smartTableData = contactData;
}, 1000);
});
}
it works..!

Categories