New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@apollo-elements/mixins

Package Overview
Dependencies
Maintainers
1
Versions
62
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@apollo-elements/mixins - npm Package Compare versions

Comparing version 1.0.0 to 1.0.2

144

apollo-element-mixin.js

@@ -10,83 +10,89 @@ import getGraphQLScriptChildDocument from '@apollo-elements/lib/get-graphql-script-child-document';

*
* @param {Class} superclass
* @return {Class}
* @param {*} superclass the class to mix into
* @return {ApolloElement~mixin} The mixed class
*/
export const ApolloElementMixin = superclass => class extends superclass {
constructor() {
super();
this.onElementMutation = this.onElementMutation.bind(this);
export const ApolloElementMixin = superclass =>
/**
* Class mixin for apollo elements
* @mixin
* @alias ApolloElementMixin~mixin
*/
class extends superclass {
constructor() {
super();
this.onElementMutation = this.onElementMutation.bind(this);
/**
* Context to be passed to link execution chain.
* @type {Object}
*/
this.context;
/**
* Context to be passed to link execution chain.
* @type {Object}
*/
this.context;
/**
* Latest data.
* @type {Object}
*/
this.data;
/**
* Latest data.
* @type {Object}
*/
this.data;
/**
* Latest error.
* @type {Object}
*/
this.error;
/**
* Latest error.
* @type {Object}
*/
this.error;
/**
* Whether a request is in flight.
* @type {Boolean}
*/
this.loading;
/**
* Whether a request is in flight.
* @type {Boolean}
*/
this.loading;
/**
* The apollo client instance.
* @type {ApolloClient}
*/
this.client = window.__APOLLO_CLIENT__;
}
/**
* The apollo client instance.
* @type {ApolloClient}
* GraphQL Document
*
* @return {DocumentNode}
*/
this.client = window.__APOLLO_CLIENT__;
}
get document() {
return this.__document || null;
}
/**
* GraphQL Document
*
* @return {DocumentNode}
*/
get document() {
return this.__document || null;
}
set document(doc) {
if (!doc) return;
if (isValidGql(doc)) {
this.__document = doc;
} else {
throw new TypeError('document must be a gql-parsed DocumentNode');
}
}
set document(doc) {
if (!doc) return;
if (isValidGql(doc)) {
this.__document = doc;
} else {
throw new TypeError('document must be a gql-parsed DocumentNode');
/** @protected */
connectedCallback() {
super.connectedCallback && super.connectedCallback();
this.elementMutationObserver = new MutationObserver(this.onElementMutation);
this.elementMutationObserver.observe(this, {
characterData: true,
childList: true,
subtree: true,
});
this.document = getGraphQLScriptChildDocument(this) || null;
}
}
/** @protected */
connectedCallback() {
super.connectedCallback && super.connectedCallback();
this.elementMutationObserver = new MutationObserver(this.onElementMutation);
this.elementMutationObserver.observe(this, {
characterData: true,
childList: true,
subtree: true,
});
this.document = getGraphQLScriptChildDocument(this) || null;
}
/** @protected */
disconnectedCallback() {
super.disconnectedCallback && super.disconnectedCallback();
this.elementMutationObserver && this.elementMutationObserver.disconnect();
this.elementMutationObserver = null;
}
/** @protected */
disconnectedCallback() {
super.disconnectedCallback && super.disconnectedCallback();
this.elementMutationObserver && this.elementMutationObserver.disconnect();
this.elementMutationObserver = null;
}
/** @protected */
onElementMutation() {
const doc = getGraphQLScriptChildDocument(this);
if (doc) this.document = doc;
}
};
/** @protected */
onElementMutation() {
const doc = getGraphQLScriptChildDocument(this);
if (doc) this.document = doc;
}
};

@@ -7,2 +7,8 @@ import { ApolloElementMixin } from './apollo-element-mixin';

/** @typedef {import('apollo-client').ErrorPolicy} ErrorPolicy */
/** @typedef {import('apollo-client').FetchPolicy} FetchPolicy */
/** @typedef {import('apollo-client').MutationUpdaterFn} MutationUpdaterFn */
/** @typedef {import('apollo-link').FetchResult} FetchResult */
/** @typedef {import('graphql').DocumentNode} DocumentNode */
const pickOptions = compose(

@@ -30,195 +36,205 @@ stripUndefinedValues,

*
* @param {Class} superclass
* @return {Class}
* @param {*} superclass the class to mix into
* @return {ApolloMutationMixin~mixin} the mixed class
*/
export const ApolloMutationMixin = superclass => class extends ApolloElementMixin(superclass) {
export const ApolloMutationMixin = superclass =>
/**
* The mutation.
*
* @return {DocumentNode}
* Class mixin for apollo-mutation elements
* @mixin
* @alias ApolloMutationMixin~mixin
*/
get mutation() {
return this.document;
}
class extends ApolloElementMixin(superclass) {
/**
* The mutation.
*
* @return {DocumentNode}
*/
get mutation() {
return this.document;
}
set mutation(mutation) {
try {
this.document = mutation;
} catch (error) {
throw new TypeError('Mutation must be a gql-parsed DocumentNode');
set mutation(mutation) {
try {
this.document = mutation;
} catch (error) {
throw new TypeError('Mutation must be a gql-parsed DocumentNode');
}
}
}
constructor() {
super();
constructor() {
super();
/**
* Whether the mutation has been called
* @type {Boolean}
*/
this.called = false;
/**
* Whether the mutation has been called
* @type {Boolean}
*/
this.called = false;
/**
* Whether to ignore the results of the mutation.
* @type {Boolean}
*/
this.ignoreResults = false;
/**
* Whether to ignore the results of the mutation.
* @type {Boolean}
*/
this.ignoreResults = false;
/**
* The ID number of the most recent mutation since the element was instantiated.
* @type {Number}
*/
this.mostRecentMutationId = 0;
/**
* The ID number of the most recent mutation since the element was instantiated.
* @type {Number}
*/
this.mostRecentMutationId = 0;
/**
* An object that represents the result of this mutation that
* will be optimistically stored before the server has actually returned a
* result.
*
* This is most often used for optimistic UI, where we want to be able to see
* the result of a mutation immediately, and update the UI later if any errors
* appear.
* @type {Object}
*/
this.optimisticResponse;
/**
* A function which updates the apollo cache when the query responds.
* This function will be called twice over the lifecycle of a mutation.
* Once at the very beginning if an optimisticResponse was provided.
* The writes created from the optimistic data will be rolled back before
* the second time this function is called which is when the mutation has
* succesfully resolved. At that point update will be called with the actual
* mutation result and those writes will not be rolled back.
*
* The reason a DataProxy is provided instead of the user calling the methods
* directly on ApolloClient is that all of the writes are batched together at
* the end of the update, and it allows for writes generated by optimistic
* data to be rolled back.
* @type {MutationUpdaterFn}
*/
this.update;
/**
* An object that maps from the name of a variable as used in the mutation GraphQL document to that variable's value.
* @type {Object}
*/
this.variables;
/**
* @protected
* @type {any}
*/
this.__explicitlySetMutation;
}
/**
* An object that represents the result of this mutation that
* will be optimistically stored before the server has actually returned a
* result.
* Increments and returns the most recent mutation id.
*
* This is most often used for optimistic UI, where we want to be able to see
* the result of a mutation immediately, and update the UI later if any errors
* appear.
* @type {Object}
* @return {number}
* @protected
*/
this.optimisticResponse;
generateMutationId() {
this.mostRecentMutationId += 1;
return this.mostRecentMutationId;
}
/**
* A function which updates the apollo cache when the query responds.
* This function will be called twice over the lifecycle of a mutation.
* Once at the very beginning if an optimisticResponse was provided.
* The writes created from the optimistic data will be rolled back before
* the second time this function is called which is when the mutation has
* succesfully resolved. At that point update will be called with the actual
* mutation result and those writes will not be rolled back.
* Returns true when an ID matches the most recent mutation id.
*
* The reason a DataProxy is provided instead of the user calling the methods
* directly on ApolloClient is that all of the writes are batched together at
* the end of the update, and it allows for writes generated by optimistic
* data to be rolled back.
* @type {MutationUpdaterFn}
* @param {number} mutationId
* @return {boolean}
* @protected
*/
this.update;
isMostRecentMutation(mutationId) {
return this.mostRecentMutationId === mutationId;
}
/**
* An object that maps from the name of a variable as used in the mutation GraphQL document to that variable's value.
* @type {Object}
* This resolves a single mutation according to the options specified and returns a Promise which is either resolved with the resulting data or rejected with an error.
*
* @param {Object} options
* @param {Object} options.context
* @param {ErrorPolicy} options.errorPolicy
* @param {FetchPolicy} options.fetchPolicy
* @param {DocumentNode} options.mutation
* @param {Object|Function} options.optimisticResponse
* @param {Array<DocumentNode>} options.refetchQueries
* @param {MutationUpdaterFn} options.update
* @param {boolean} options.awaitRefetchQueries
* @param {Object} options.variables
* @return {Promise<FetchResult>}
*/
this.variables;
async mutate({
mutation = this.mutation,
variables = this.variables,
...options
} = this) {
const mutationId = this.generateMutationId();
this.__explicitlySetMutation;
}
this.loading = true;
this.error = undefined;
this.data = undefined;
this.called = true;
/**
* Increments and returns the most recent mutation id.
*
* @return {number}
* @protected
*/
generateMutationId() {
this.mostRecentMutationId += 1;
return this.mostRecentMutationId;
}
/**
* Returns true when an ID matches the most recent mutation id.
*
* @param {number} mutationId
* @return {boolean}
* @protected
*/
isMostRecentMutation(mutationId) {
return this.mostRecentMutationId === mutationId;
}
/**
* This resolves a single mutation according to the options specified and returns a Promise which is either resolved with the resulting data or rejected with an error.
*
* @param {Object} options
* @param {Object} options.context
* @param {ErrorPolicy} options.errorPolicy
* @param {FetchPolicy} options.fetchPolicy
* @param {DocumentNode} options.mutation
* @param {Object|Function} options.optimisticResponse
* @param {Array<DocumentNode>} options.refetchQueries
* @param {UpdateFunction} options.update
* @param {boolean} options.awaitRefetchQueries
* @param {Object} options.variables
* @return {Promise<FetchResult>}
*/
async mutate({
mutation = this.mutation,
variables = this.variables,
...options
} = this) {
const mutationId = this.generateMutationId();
this.loading = true;
this.error = undefined;
this.data = undefined;
this.called = true;
try {
const response =
await this.client.mutate(pickOptions({
mutation,
variables,
...options,
}));
this.onCompletedMutation(response, mutationId);
return response;
} catch (error) {
this.onMutationError(error, mutationId);
return error;
try {
const response =
await this.client.mutate(pickOptions({
mutation,
variables,
...options,
}));
this.onCompletedMutation(response, mutationId);
return response;
} catch (error) {
this.onMutationError(error, mutationId);
return error;
}
}
}
/**
* Callback for when a mutation is completed.
*
* @param {FetchResult} response
* @param {number} mutationId
* @return {any}
* @protected
*/
onCompletedMutation(response, mutationId) {
const { data, errors = [] } = response;
const error = errors.length ? new ApolloError({ graphQLErrors: errors }) : null;
if (this.isMostRecentMutation(mutationId) && !this.ignoreResults) {
this.loading = false;
this.error = error;
this.data = data;
/**
* Callback for when a mutation is completed.
*
* @param {FetchResult} response
* @param {number} mutationId
* @return {any}
* @protected
*/
onCompletedMutation(response, mutationId) {
const { data, errors = [] } = response;
const error = errors.length ? new ApolloError({ graphQLErrors: errors }) : null;
if (this.isMostRecentMutation(mutationId) && !this.ignoreResults) {
this.loading = false;
this.error = error;
this.data = data;
}
return this.onCompleted(data);
}
return this.onCompleted(data);
}
/**
* Callback for when a mutation fails.
*
* @param {Error} error mutation error
* @param {number} mutationId id of the mutation
* @return {any}
* @protected
*/
onMutationError(error, mutationId) {
if (this.isMostRecentMutation(mutationId)) {
this.loading = false;
this.data = null;
this.error = error;
/**
* Callback for when a mutation fails.
*
* @param {ApolloError} error mutation error
* @param {number} mutationId id of the mutation
* @return {any}
* @protected
*/
onMutationError(error, mutationId) {
if (this.isMostRecentMutation(mutationId)) {
this.loading = false;
this.data = null;
this.error = error;
}
return this.onError(error);
}
return this.onError(error);
}
/**
* Callback for when a mutation is completed.
*
* @abstract
*/
onCompleted() { }
/**
* Callback for when a mutation is completed.
*
* @abstract
*/
onCompleted() { }
/**
* Callback for when an error occurs in mutation.
*
* @abstract
*/
onError() { }
};
/**
* Callback for when an error occurs in mutation.
*
* @abstract
*/
onError() { }
};

@@ -7,2 +7,16 @@ import { ApolloElementMixin } from './apollo-element-mixin.js';

/** @typedef {import('apollo-client').ApolloError} ApolloError */
/** @typedef {import('apollo-client').ApolloQueryResult} ApolloQueryResult */
/** @typedef {import('apollo-client').ErrorPolicy} ErrorPolicy */
/** @typedef {import('apollo-client').FetchMoreQueryOptions} FetchMoreQueryOptions */
/** @typedef {import('apollo-client').FetchPolicy} FetchPolicy */
/** @typedef {import('apollo-client').ObservableQuery} ObservableQuery */
/** @typedef {import('apollo-client').QueryOptions} QueryOptions */
/** @typedef {import('apollo-client').SubscribeToMoreOptions} SubscribeToMoreOptions */
/** @typedef {import('apollo-client').SubscriptionOptions} SubscriptionOptions */
/** @typedef {import('apollo-client').WatchQueryOptions} WatchQueryOptions */
/** @typedef {import('apollo-client/core/watchQueryOptions').ModifiableWatchQueryOptions} ModifiableWatchQueryOptions */
/** @typedef {import('graphql').DocumentNode} DocumentNode */
/** @typedef {import('zen-observable')} Observable */
const pickExecuteQueryOpts = compose(

@@ -43,283 +57,277 @@ stripUndefinedValues,

*
* @param {Class} superclass
* @return {ApolloQuery}
* @param {*} superclass
* @return {ApolloQueryMixin~mixin}
*/
export const ApolloQueryMixin = superclass => class extends ApolloElementMixin(superclass) {
export const ApolloQueryMixin = superclass =>
/**
* A GraphQL document containing a single query.
*
* @return {DocumentNode}
* Class mixin for apollo-query elements
* @mixin
* @template TData
* @template TVariables
* @alias ApolloQueryMixin~mixin
*/
get query() {
return this.document;
}
class extends ApolloElementMixin(superclass) {
/**
* A GraphQL document containing a single query.
*
* @return {DocumentNode}
*/
get query() {
return this.document;
}
set query(query) {
try {
this.document = query;
} catch (error) {
throw new TypeError('Query must be a gql-parsed DocumentNode');
set query(query) {
try {
this.document = query;
} catch (error) {
throw new TypeError('Query must be a gql-parsed DocumentNode');
}
if (!this.noAutoSubscribe && query) {
this.subscribe({ query, variables: this.variables });
}
}
if (!this.noAutoSubscribe && query) {
this.subscribe({ query, variables: this.variables });
/**
* An object map from variable name to variable value, where the variables are used within the GraphQL query.
*
* @return {Object<string, *>}
*/
get variables() {
return this.__variables;
}
}
/**
* An object map from variable name to variable value, where the variables are used within the GraphQL query.
*
* @return {Object<string, *>}
*/
get variables() {
return this.__variables;
}
set variables(variables) {
this.__variables = variables;
const query = this.__query;
this.observableQuery
? this.setVariables(variables)
: this.subscribe({ query, variables });
}
set variables(variables) {
this.__variables = variables;
const query = this.__query;
this.observableQuery
? this.setVariables(variables)
: this.subscribe({ query, variables });
}
/**
* Exposes the [`ObservableQuery#setOptions`](https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.setOptions) method.
*
* @return {ModifiableWatchQueryOptions} options [options](https://www.apollographql.com/docs/react/api/apollo-client.html#ModifiableWatchQueryOptions) object.
*/
get options() {
return this.__options;
}
/**
* Exposes the [`ObservableQuery#setOptions`](https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.setOptions) method.
*
* @return {ModifiableWatchQueryOptions} options [options](https://www.apollographql.com/docs/react/api/apollo-client.html#ModifiableWatchQueryOptions) object.
*/
get options() {
return this.__options;
}
set options(options) {
this.__options = options;
this.observableQuery && this.observableQuery.setOptions(options);
}
set options(options) {
this.__options = options;
this.observableQuery && this.observableQuery.setOptions(options);
}
constructor() {
super();
this.nextData = this.nextData.bind(this);
this.nextError = this.nextError.bind(this);
constructor() {
super();
this.nextData = this.nextData.bind(this);
this.nextError = this.nextError.bind(this);
/**
* Specifies the ErrorPolicy to be used for this query.
* @type {ErrorPolicy}
*/
this.errorPolicy = 'none';
/**
* Specifies the ErrorPolicy to be used for this query.
* @type {ErrorPolicy}
*/
this.errorPolicy = 'none';
/**
* Specifies the FetchPolicy to be used for this query.
* @type {FetchPolicy}
*/
this.fetchPolicy = 'cache-first';
/**
* Specifies the FetchPolicy to be used for this query.
* @type {FetchPolicy}
*/
this.fetchPolicy = 'cache-first';
/**
* Whether or not to fetch results.
* @type {Boolean}
*/
this.fetchResults = undefined;
/**
* The time interval (in milliseconds) on which this query should be refetched from the server.
* @type {Number}
*/
this.pollInterval = undefined;
/**
* Whether or not updates to the network status should trigger next on the observer of this query.
* @type {Boolean}
*/
this.notifyOnNetworkStatusChange = undefined;
/**
* Try and fetch new results even if the variables haven't changed (we may still just hit the store, but if there's nothing in there will refetch).
* @type {Boolean}
*/
this.tryFetch = undefined;
/**
* The apollo ObservableQuery watching this element's query.
* @type {ObservableQuery}
*/
this.observableQuery;
}
/** @protected */
connectedCallback() {
super.connectedCallback && super.connectedCallback();
if (this.query) this.subscribe();
}
/**
* Whether or not to fetch results.
* @type {Boolean}
* Exposes the [`ObservableQuery#refetch`](https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.refetch) method.
*
* @param {Object} variables The new set of variables. If there are missing variables, the previous values of those variables will be used..
* @return {Promise<ApolloQueryResult>}
*/
this.fetchResults = undefined;
refetch(...args) {
return (
this.observableQuery &&
this.observableQuery.refetch(...args)
);
}
/**
* The time interval (in milliseconds) on which this query should be refetched from the server.
* @type {Number}
* Exposes the [`ObservableQuery#setVariables`](https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.setVariables) method.
*
* @deprecated: This method on ObservableQuery is meant to be private. It will be removed.
* @param {Object} variables The new set of variables. If there are missing variables, the previous values of those variables will be used.
* @param {boolean=} tryFetch Try and fetch new results even if the variables haven't changed (we may still just hit the store, but if there's nothing in there will refetch).
* @param {boolean=} fetchResults Option to ignore fetching results when updating variables.
* @return {Promise<ApolloQueryResult>}
*/
this.pollInterval = undefined;
setVariables(variables, tryFetch = this.tryFetch, fetchResults = this.fetchResults) {
return (
this.observableQuery &&
this.observableQuery.setVariables(variables, tryFetch, fetchResults)
);
}
/**
* Whether or not updates to the network status should trigger next on the observer of this query.
* @type {Boolean}
* Resets the observableQuery and subscribes.
*
* @param {SubscriptionOptions=} params
* @return {Promise<ZenObservable.Observer<ApolloQueryResult>>}
*/
this.notifyOnNetworkStatusChange = undefined;
async subscribe({ query = this.query, variables = this.variables } = {}) {
if (!hasAllVariables({ query, variables })) return;
this.observableQuery = this.watchQuery({ query, variables });
return this.observableQuery.subscribe({
next: this.nextData,
error: this.nextError,
});
}
/**
* Try and fetch new results even if the variables haven't changed (we may still just hit the store, but if there's nothing in there will refetch).
* @type {Boolean}
* Lets you pass a GraphQL subscription and updateQuery function
* to subscribe to more updates for your query.
*
* The `updateQuery` parameter is a function that takes the previous query data,
* then a `{ subscriptionData: TSubscriptionResult }` object,
* and returns an object with updated query data based on the new results.
*
* @param {SubscribeToMoreOptions=} options
* @return {function(): void}
*/
this.tryFetch = undefined;
subscribeToMore(options) {
return (
this.observableQuery &&
this.observableQuery.subscribeToMore(options)
);
}
/**
* The apollo ObservableQuery watching this element's query.
* @type {ObservableQuery}
* Executes a Query once and updates the component with the result
*
* @param {QueryOptions} options
* @return {Promise<ApolloQueryResult>}
*/
this.observableQuery;
}
executeQuery({
query = this.query,
variables = this.variables,
...options
} = this) {
const opts = pickExecuteQueryOpts({ ...this, ...options, query, variables });
/** @protected */
connectedCallback() {
super.connectedCallback && super.connectedCallback();
if (this.query) this.subscribe();
}
const queryPromise =
this.client
.query(opts)
.catch(this.nextError.bind(this));
/**
* Exposes the [`ObservableQuery#refetch`](https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.refetch) method.
*
* @param {Object} variables The new set of variables. If there are missing variables, the previous values of those variables will be used..
* @return {Promise<ApolloQueryResult>}
*/
refetch(...args) {
return (
this.observableQuery &&
this.observableQuery.refetch(...args)
);
}
queryPromise.then(this.nextData.bind(this));
/**
* Exposes the [`ObservableQuery#setVariables`](https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.setVariables) method.
*
* @deprecated: This method on ObservableQuery is meant to be private. It will be removed.
* @param {Object} variables The new set of variables. If there are missing variables, the previous values of those variables will be used.
* @param {boolean=} tryFetch Try and fetch new results even if the variables haven't changed (we may still just hit the store, but if there's nothing in there will refetch).
* @param {boolean=} fetchResults Option to ignore fetching results when updating variables.
* @return {Promise<ApolloQueryResult>}
*/
setVariables(variables, tryFetch = this.tryFetch, fetchResults = this.fetchResults) {
return (
this.observableQuery &&
this.observableQuery.setVariables(variables, tryFetch, fetchResults)
);
}
return queryPromise;
}
/**
* Resets the observableQuery and subscribes.
*
* @param {{query: DocumentNode, variables: Object}=} params
* @return {Promise<ZenObservable.Observer<ApolloQueryResult<TData>>>}
*/
async subscribe({ query = this.query, variables = this.variables } = {}) {
if (!hasAllVariables({ query, variables })) return;
this.observableQuery = this.watchQuery({ query, variables });
return this.observableQuery.subscribe({
next: this.nextData,
error: this.nextError,
});
}
/**
* Exposes the `ObservableQuery#fetchMore` method.
* https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.fetchMore
*
* The optional `updateQuery` parameter is a function that takes the previous query data,
* then a `{ subscriptionData: TSubscriptionResult }` object,
* and returns an object with updated query data based on the new results.
*
* The optional `variables` parameter is an optional new variables object.
*
* @param {FetchMoreQueryOptions=} params
* @return {Promise<ApolloQueryResult>}
*/
fetchMore({ query = this.query, updateQuery, variables } = {}) {
return (
this.observableQuery &&
this.observableQuery.fetchMore({ query, updateQuery, variables })
);
}
/**
* Lets you pass a GraphQL subscription and updateQuery function
* to subscribe to more updates for your query.
*
* The `updateQuery` parameter is a function that takes the previous query data,
* then a `{ subscriptionData: TSubscriptionResult }` object,
* and returns an object with updated query data based on the new results.
*
* @param {{document: DocumentNode, updateQuery: Function, variables: Object, onError: Function}=} options
* @return {function(): void}
*/
subscribeToMore(options) {
return (
this.observableQuery &&
this.observableQuery.subscribeToMore(options)
);
}
/**
* Creates an instance of ObservableQuery with the options provided by the element.
* - `context` Context to be passed to link execution chain
* - `errorPolicy` Specifies the ErrorPolicy to be used for this query
* - `fetchPolicy` Specifies the FetchPolicy to be used for this query
* - `fetchResults` Whether or not to fetch results
* - `metadata` Arbitrary metadata stored in the store with this query. Designed for debugging, developer tools, etc.
* - `notifyOnNetworkStatusChange` Whether or not updates to the network status should trigger next on the observer of this query
* - `pollInterval` The time interval (in milliseconds) on which this query should be refetched from the server.
* - `query` A GraphQL document that consists of a single query to be sent down to the server.
* - `variables` A map going from variable name to variable value, where the variables are used within the GraphQL query.
*
* @param {WatchQueryOptions=} options
*
* @return {ObservableQuery}
* @protected
*/
watchQuery({
query = this.query,
variables = this.variables,
...options
} = this) {
const opts = pickOpts({ ...this, ...options, query, variables });
return this.client.watchQuery(opts);
}
/**
* Executes a Query once and updates the component with the result
*
* @param {{ metadata: Object, context: Object, query: DocumentNode, variables: Object, fetchPolicy: FetchPolicy, errorPolicy: ErrorPolicy, fetchResults: boolean }=} options
* @return {Promise<ApolloQueryResult>}
*/
executeQuery({
query = this.query,
variables = this.variables,
...options
} = this) {
const opts = pickExecuteQueryOpts({ ...this, ...options, query, variables });
/**
* Updates the element with the result of a query.
*
* @param {ApolloQueryResult} result The result of the query.
* @param {Object} result.data The data from the query.
* @param {boolean} result.loading Whether the query is loading.
* @param {number} result.networkStatus The networkStatus of the query.
* @param {boolean} result.stale Whether the query is stale.
* @protected
*/
nextData({ data, loading, networkStatus, stale }) {
this.data = data;
this.loading = loading;
this.networkStatus = networkStatus;
this.stale = stale;
}
const queryPromise =
this.client
.query(opts)
.catch(this.nextError.bind(this));
queryPromise.then(this.nextData.bind(this));
return queryPromise;
}
/**
* Exposes the `ObservableQuery#fetchMore` method.
* https://www.apollographql.com/docs/react/api/apollo-client.html#ObservableQuery.fetchMore
*
* The optional `updateQuery` parameter is a function that takes the previous query data,
* then a `{ subscriptionData: TSubscriptionResult }` object,
* and returns an object with updated query data based on the new results.
*
* The optional `variables` parameter is an optional new variables object.
*
* @param {{
* query: DocumentNode,
* updateQuery: function(prev: TData, { subscriptionData: TSubscriptionResult }): TData,
* variables: Object
* }=} params
* @return {Promise<ApolloQueryResult>}
*/
fetchMore({ query = this.query, updateQuery, variables } = {}) {
return (
this.observableQuery &&
this.observableQuery.fetchMore({ query, updateQuery, variables })
);
}
/**
* Creates an instance of ObservableQuery with the options provided by the element.
* - `context` Context to be passed to link execution chain
* - `errorPolicy` Specifies the ErrorPolicy to be used for this query
* - `fetchPolicy` Specifies the FetchPolicy to be used for this query
* - `fetchResults` Whether or not to fetch results
* - `metadata` Arbitrary metadata stored in the store with this query. Designed for debugging, developer tools, etc.
* - `notifyOnNetworkStatusChange` Whether or not updates to the network status should trigger next on the observer of this query
* - `pollInterval` The time interval (in milliseconds) on which this query should be refetched from the server.
* - `query` A GraphQL document that consists of a single query to be sent down to the server.
* - `variables` A map going from variable name to variable value, where the variables are used within the GraphQL query.
*
* @param {{
* context: Object,
* errorPolicy: ErrorPolicy,
* fetchPolicy: FetchPolicy,
* fetchResults: boolean,
* metadata: Object,
* notifyOnNetworkStatusChange: boolean,
* pollInterval: number,
* query: DocumentNode,
* variables: Object,
* }=} options
*
* @return {ObservableQuery}
* @protected
*/
watchQuery({
query = this.query,
variables = this.variables,
...options
} = this) {
const opts = pickOpts({ ...this, ...options, query, variables });
return this.client.watchQuery(opts);
}
/**
* Updates the element with the result of a query.
*
* @param {ApolloQueryResult} result The result of the query.
* @param {Object} result.data The data from the query.
* @param {boolean} result.loading Whether the query is loading.
* @param {number} result.networkStatus The networkStatus of the query.
* @param {boolean} result.stale Whether the query is stale.
* @protected
*/
nextData({ data, loading, networkStatus, stale }) {
this.data = data;
this.loading = loading;
this.networkStatus = networkStatus;
this.stale = stale;
}
/**
* Updates the element with the error when the query fails.
*
* @param {Error} error
* @protected
*/
nextError(error) {
this.error = error;
}
};
/**
* Updates the element with the error when the query fails.
*
* @param {Error} error
* @protected
*/
nextError(error) {
this.error = error;
}
};

@@ -132,15 +132,33 @@ import { chai, expect, html } from '@open-wc/testing';

describe('setting variables', function describeSetVariables() {
it('does nothing when there is no observableQuery', async function setVariablesNoQuery() {
const el = await getElement({ client });
el.variables = { errorPolicy: 'foo' };
expect(el.variables).to.deep.equal({ errorPolicy: 'foo' });
describe('without observableQuery', function() {
let el;
beforeEach(async function() {
el = await getElement({ client });
el.variables = { errorPolicy: 'foo' };
});
it('does nothing', async function setVariablesNoQuery() {
expect(el.variables).to.deep.equal({ errorPolicy: 'foo' });
});
});
describe('with an observableQuery', function() {
let el;
let setVariablesSpy;
const query = gql`query { foo }`;
it('calls observableQuery.subscribe when there is a query', async function setVariablesCallsObservableQuerySetVariables() {
const query = gql`query { foo }`;
const el = await getElement({ client, query });
const setVariablesSpy = stub(el.observableQuery, 'setVariables');
// shouldn't this be an instance of ObservableQuery?
el.variables = { errorPolicy: 'foo' };
expect(setVariablesSpy).to.have.been.calledWith({ errorPolicy: 'foo' });
beforeEach(async function() {
el = await getElement({ client, query });
setVariablesSpy = spy(el.observableQuery, 'setVariables');
});
afterEach(function() {
setVariablesSpy.restore();
});
it('calls observableQuery.subscribe', async function setVariablesCallsObservableQuerySetVariables() {
// shouldn't this be an instance of ObservableQuery?
el.variables = { errorPolicy: 'foo' };
expect(setVariablesSpy).to.have.been.calledWith(match({ errorPolicy: 'foo' }));
});
});

@@ -340,4 +358,2 @@ });

};
// HACK: oof
client.initQueryManager();

@@ -344,0 +360,0 @@ const watchQueryStub = stub(client.queryManager, 'watchQuery');

@@ -6,3 +6,17 @@ import isFunction from 'crocks/predicates/isFunction';

/** @typedef {import('apollo-client').FetchPolicy} FetchPolicy */
/** @typedef {import('apollo-client').ApolloError} ApolloError */
/** @typedef {import('apollo-client').SubscriptionOptions} SubscriptionOptions */
/** @typedef {import('graphql').DocumentNode} DocumentNode */
/** @typedef {import('zen-observable')} Observable */
/**
* @typedef {Object} SubscriptionResult
* @template {Object} TData
* @property {Boolean} loading whether the subscription is loading
* @property {TData} data subscription data
* @property {ApolloError} error subscription error
*/
/**
* `ApolloSubscriptionMixin`: class mixin for apollo-subscription elements.

@@ -14,133 +28,140 @@ *

*
* @param {class} superclass
* @return {class}
* @param {*} superclass the class to mix in to
* @return {ApolloSubscriptionMixin~mixin} the mixed class
*/
export const ApolloSubscriptionMixin = superclass => class extends ApolloElementMixin(superclass) {
export const ApolloSubscriptionMixin = superclass =>
/**
* A GraphQL document containing a single subscription.
*
* @return {DocumentNode}
* Class mixin for apollo-subscription elements
* @mixin
* @alias ApolloSubscriptionMixin~mixin
*/
get subscription() {
return this.document;
}
class extends ApolloElementMixin(superclass) {
/**
* A GraphQL document containing a single subscription.
*
* @return {DocumentNode}
*/
get subscription() {
return this.document;
}
set subscription(subscription) {
try {
this.document = subscription;
} catch (error) {
throw new TypeError('Subscription must be a gql-parsed DocumentNode');
set subscription(subscription) {
try {
this.document = subscription;
} catch (error) {
throw new TypeError('Subscription must be a gql-parsed DocumentNode');
}
if (subscription && !this.observable) this.subscribe();
}
if (subscription && !this.observable) this.subscribe();
}
/**
* An object map from variable name to variable value, where the variables are used within the GraphQL subscription.
*
* @return {Object<string, *>}
*/
get variables() {
return this.__variables;
}
/**
* An object map from variable name to variable value, where the variables are used within the GraphQL subscription.
*
* @return {Object<string, *>}
*/
get variables() {
return this.__variables;
}
set variables(variables) {
this.__variables = variables;
if (!this.observable) this.subscribe();
}
set variables(variables) {
this.__variables = variables;
if (!this.observable) this.subscribe();
}
constructor() {
super();
this.nextData = this.nextData.bind(this);
this.nextError = this.nextError.bind(this);
constructor() {
super();
this.nextData = this.nextData.bind(this);
this.nextError = this.nextError.bind(this);
/**
* Specifies the FetchPolicy to be used for this subscription.
* @type {FetchPolicy}
*/
this.fetchPolicy = 'cache-first';
/**
* Specifies the FetchPolicy to be used for this subscription.
* @type {FetchPolicy}
*/
this.fetchPolicy = 'cache-first';
/**
* Whether or not to fetch results.
* @type {Boolean}
*/
this.fetchResults = undefined;
/**
* Whether or not to fetch results.
* @type {Boolean}
*/
this.fetchResults = undefined;
/**
* The time interval (in milliseconds) on which this subscription should be refetched from the server.
* @type {Number}
*/
this.pollInterval = undefined;
/**
* The time interval (in milliseconds) on which this subscription should be refetched from the server.
* @type {Number}
*/
this.pollInterval = undefined;
/**
* Whether or not updates to the network status should trigger next on the observer of this subscription.
* @type {Boolean}
*/
this.notifyOnNetworkStatusChange = undefined;
/**
* Try and fetch new results even if the variables haven't changed (we may still just hit the store, but if there's nothing in there will refetch).
* @type {Boolean}
*/
this.tryFetch = undefined;
/**
* Observable watching this element's subscription.
* @type {Observable}
*/
this.observable;
}
/** @protected */
connectedCallback() {
super.connectedCallback && super.connectedCallback();
this.subscribe();
}
/**
* Whether or not updates to the network status should trigger next on the observer of this subscription.
* @type {Boolean}
* Resets the observable and subscribes.
*
* @template {Object} TData
* @param {SubscriptionOptions} options
* @return {Promise<Observer<SubscriptionResult<TData>>>}
*/
this.notifyOnNetworkStatusChange = undefined;
async subscribe({
fetchPolicy = this.fetchPolicy,
query = this.subscription,
variables = this.variables,
} = {}) {
if (!hasAllVariables({ query, variables })) return;
this.observable = this.client.subscribe(
stripUndefinedValues({ query, variables, fetchPolicy })
);
return this.observable.subscribe({
next: this.nextData,
error: this.nextError,
});
}
/**
* Try and fetch new results even if the variables haven't changed (we may still just hit the store, but if there's nothing in there will refetch).
* @type {Boolean}
* Updates the element with the result of a subscription.
*
* @template TData
* @param {SubscriptionResult<TData>} result The result of the subscription.
* @protected
*/
this.tryFetch = undefined;
nextData({ data }) {
const { client, onSubscriptionData } = this;
const subscriptionData = { data };
if (isFunction(onSubscriptionData)) onSubscriptionData({ client, subscriptionData });
this.data = data;
this.loading = false;
this.error = undefined;
}
/**
* Observable watching this element's subscription.
* @type {Observable}
* Updates the element with the error when the subscription fails.
*
* @param {ApolloError} error
* @protected
*/
this.observable;
}
/** @protected */
connectedCallback() {
super.connectedCallback && super.connectedCallback();
this.subscribe();
}
/**
* Resets the observable and subscribes.
*
* @param {{fetchPolicy: FetchPolicy, query: DocumentNode, variables: Object}} options
* @return {Promise<ZenObservable.Observer<SubscriptionResult<TData>>>}
*/
async subscribe({
fetchPolicy = this.fetchPolicy,
query = this.subscription,
variables = this.variables,
} = {}) {
if (!hasAllVariables({ query, variables })) return;
this.observable = this.client.subscribe(
stripUndefinedValues({ query, variables, fetchPolicy })
);
return this.observable.subscribe({
next: this.nextData,
error: this.nextError,
});
}
/**
* Updates the element with the result of a subscription.
*
* @param {ApolloQueryResult} result The result of the subscription.
* @param {Object} result.data The data from the subscription.
* @param {boolean} result.loading Whether the subscription is loading.
* @protected
*/
nextData({ data }) {
const { client, onSubscriptionData } = this;
if (isFunction(onSubscriptionData)) onSubscriptionData({ client, subscriptionData: { data } });
this.data = data;
this.loading = false;
this.error = undefined;
}
/**
* Updates the element with the error when the subscription fails.
*
* @param {Error} error
* @protected
*/
nextError(error) {
this.error = error;
this.loading = false;
}
};
nextError(error) {
this.error = error;
this.loading = false;
}
};

@@ -6,2 +6,24 @@ # Change Log

## [1.0.2](https://github.com/apollo-elements/apollo-elements/compare/@apollo-elements/mixins@1.0.1...@apollo-elements/mixins@1.0.2) (2019-06-06)
### Bug Fixes
* **mixins:** improved type docs ([0ac2a3a](https://github.com/apollo-elements/apollo-elements/commit/0ac2a3a))
## [1.0.1](https://github.com/apollo-elements/apollo-elements/compare/@apollo-elements/mixins@0.0.11...@apollo-elements/mixins@1.0.1) (2019-05-26)
### Bug Fixes
* **mixins:** allow default options in queries ([4a8895e](https://github.com/apollo-elements/apollo-elements/commit/4a8895e))
# [1.0.0](https://github.com/apollo-elements/apollo-elements/compare/@apollo-elements/mixins@0.0.11...@apollo-elements/mixins@1.0.0) (2019-04-03)

@@ -8,0 +30,0 @@

{
"name": "@apollo-elements/mixins",
"version": "1.0.0",
"version": "1.0.2",
"description": "👩‍🚀🌛 Custom Element class mixins for Apollo GraphQL 🚀👨‍🚀",

@@ -31,12 +31,7 @@ "main": "index.js",

"dependencies": {
"@apollo-elements/lib": "^1.0.0",
"apollo-client": "^2.4.12",
"@apollo-elements/lib": "^1.0.1",
"apollo-client": "^2.6.0",
"crocks": "^0.11.1"
},
"devDependencies": {
"@apollo-elements/test-helpers": "^0.0.3",
"@types/crocks": "^0.0.2",
"graphql": "^14.1.1"
},
"gitHead": "36a6a137c69795087edb47cb780008f4a43b06ee"
"gitHead": "44b65b4a0aa6d5c5ed1d0c8ba403879ae9a4fba2"
}

@@ -43,2 +43,6 @@ # @apollo-elements/mixins

## Aren't Mixins Considered Harmful?
Different kind of mixin. These are [JS class mixins](http://justinfagnani.com/2015/12/21/real-mixins-with-javascript-classes/).
## 😎 Cool Tricks

@@ -45,0 +49,0 @@

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc