001package com.box.sdk;
002
003import java.net.MalformedURLException;
004import java.net.URL;
005import java.util.Date;
006import java.util.Iterator;
007import java.util.LinkedHashSet;
008import java.util.Set;
009
010import com.eclipsesource.json.JsonArray;
011import com.eclipsesource.json.JsonObject;
012import com.eclipsesource.json.JsonValue;
013
014/**
015 * A log of events that were retrieved from the events endpoint.
016 *
017 * <p>An EventLog cannot be instantiated directly. Instead, use one of the static methods to retrieve a log of events.
018 * Unlike the {@link EventStream} class, EventLog doesn't support retrieving events in real-time.
019 * </p>
020 */
021public class EventLog implements Iterable<BoxEvent> {
022    private static final int ENTERPRISE_LIMIT = 500;
023    private static final URLTemplate ENTERPRISE_EVENT_URL_TEMPLATE = new URLTemplate("events?stream_type=admin_logs&"
024        + "limit=" + ENTERPRISE_LIMIT);
025
026    private final int chunkSize;
027    private final int limit;
028    private final String nextStreamPosition;
029    private final String streamPosition;
030    private final Set<BoxEvent> set;
031
032    private Date startDate;
033    private Date endDate;
034
035    EventLog(BoxAPIConnection api, JsonObject json, String streamPosition, int limit) {
036        this.streamPosition = streamPosition;
037        this.limit = limit;
038        this.nextStreamPosition = json.get("next_stream_position").asString();
039        this.chunkSize = json.get("chunk_size").asInt();
040
041        this.set = new LinkedHashSet<BoxEvent>(this.chunkSize);
042        JsonArray entries = json.get("entries").asArray();
043        for (JsonValue entry : entries) {
044            this.set.add(new BoxEvent(api, entry.asObject()));
045        }
046    }
047
048    /**
049     * Gets all the enterprise events that occurred within a specified date range.
050     * @param  api    the API connection to use.
051     * @param  after  the lower bound on the timestamp of the events returned.
052     * @param  before the upper bound on the timestamp of the events returned.
053     * @param  types  an optional list of event types to filter by.
054     * @return        a log of all the events that met the given criteria.
055     */
056    public static EventLog getEnterpriseEvents(BoxAPIConnection api, Date after, Date before, BoxEvent.Type... types) {
057        return getEnterpriseEvents(api, null, after, before, types);
058    }
059
060    /**
061     * Gets all the enterprise events that occurred within a specified date range, starting from a given position
062     * within the event stream.
063     * @param  api      the API connection to use.
064     * @param  position the starting position of the event stream.
065     * @param  after    the lower bound on the timestamp of the events returned.
066     * @param  before   the upper bound on the timestamp of the events returned.
067     * @param  types    an optional list of event types to filter by.
068     * @return          a log of all the events that met the given criteria.
069     */
070    public static EventLog getEnterpriseEvents(BoxAPIConnection api, String position, Date after, Date before,
071        BoxEvent.Type... types) {
072
073        URL url = ENTERPRISE_EVENT_URL_TEMPLATE.build(api.getBaseURL());
074
075        if (position != null || types.length > 0 || after != null
076            || before != null) {
077            QueryStringBuilder queryBuilder = new QueryStringBuilder(url.getQuery());
078
079            if (after != null) {
080                queryBuilder.appendParam("created_after",
081                    BoxDateFormat.format(after));
082            }
083
084            if (before != null) {
085                queryBuilder.appendParam("created_before",
086                    BoxDateFormat.format(before));
087            }
088
089            if (position != null) {
090                queryBuilder.appendParam("stream_position", position);
091            }
092
093            if (types.length > 0) {
094                StringBuilder filterBuilder = new StringBuilder();
095                for (BoxEvent.Type filterType : types) {
096                    filterBuilder.append(filterType.name());
097                    filterBuilder.append(',');
098                }
099                filterBuilder.deleteCharAt(filterBuilder.length() - 1);
100                queryBuilder.appendParam("event_type", filterBuilder.toString());
101            }
102
103            try {
104                url = queryBuilder.addToURL(url);
105            } catch (MalformedURLException e) {
106                throw new BoxAPIException("Couldn't append a query string to the provided URL.");
107            }
108        }
109
110        BoxAPIRequest request = new BoxAPIRequest(api, url, "GET");
111        BoxJSONResponse response = (BoxJSONResponse) request.send();
112        JsonObject responseJSON = JsonObject.readFrom(response.getJSON());
113        EventLog log = new EventLog(api, responseJSON, position, ENTERPRISE_LIMIT);
114        log.setStartDate(after);
115        log.setEndDate(before);
116        return log;
117    }
118
119    void setStartDate(Date startDate) {
120        this.startDate = startDate;
121    }
122
123    void setEndDate(Date endDate) {
124        this.endDate = endDate;
125    }
126
127    /**
128     * Returns an iterator over the events in this log.
129     * @return an iterator over the events in this log.
130     */
131    @Override
132    public Iterator<BoxEvent> iterator() {
133        return this.set.iterator();
134    }
135
136    /**
137     * Gets the date of the earliest event in this log.
138     *
139     * <p>The value returned by this method corresponds to the <code>created_after</code> URL parameter that was used
140     * when retrieving the events in this EventLog.</p>
141     *
142     * @return the date of the earliest event in this log.
143     */
144    public Date getStartDate() {
145        return this.startDate;
146    }
147
148    /**
149     * Gets the date of the latest event in this log.
150     *
151     * <p>The value returned by this method corresponds to the <code>created_before</code> URL parameter that was used
152     * when retrieving the events in this EventLog.</p>
153     *
154     * @return the date of the latest event in this log.
155     */
156    public Date getEndDate() {
157        return this.endDate;
158    }
159
160    /**
161     * Gets the maximum number of events that this event log could contain given its start date, end date, and stream
162     * position.
163     *
164     * <p>The value returned by this method corresponds to the <code>limit</code> URL parameter that was used when
165     * retrieving the events in this EventLog.</p>
166     *
167     * @return the maximum number of events.
168     */
169    public int getLimit() {
170        return this.limit;
171    }
172
173    /**
174     * Gets the starting position of the events in this log within the event stream.
175     *
176     * <p>The value returned by this method corresponds to the <code>stream_position</code> URL parameter that was used
177     * when retrieving the events in this EventLog.</p>
178     *
179     * @return the starting position within the event stream.
180     */
181    public String getStreamPosition() {
182        return this.streamPosition;
183    }
184
185    /**
186     * Gets the next position within the event stream for retrieving subsequent events.
187     *
188     * <p>The value returned by this method corresponds to the <code>next_stream_position</code> field returned by the
189     * API's events endpoint.</p>
190     *
191     * @return the next position within the event stream.
192     */
193    public String getNextStreamPosition() {
194        return this.nextStreamPosition;
195    }
196
197    /**
198     * Gets the number of events in this log, including duplicate events.
199     *
200     * <p>The chunk size may not be representative of the number of events returned by this EventLog's iterator because
201     * the iterator will automatically ignore duplicate events.</p>
202     *
203     * <p>The value returned by this method corresponds to the <code>chunk_size</code> field returned by the API's
204     * events endpoint.</p>
205     *
206     * @return the number of events, including duplicates.
207     */
208    public int getChunkSize() {
209        return this.chunkSize;
210    }
211
212    /**
213     * Gets the number of events in this list, excluding duplicate events.
214     *
215     * <p>The size is guaranteed to be representative of the number of events returned by this EventLog's iterator.</p>
216     *
217     * @return the number of events, excluding duplicates.
218     */
219    public int getSize() {
220        return this.set.size();
221    }
222}